2011-05-14 3 views
16

En jouant avec Dapper, je suis assez content des résultats obtenus jusqu'à présent - intrigant! Mais maintenant, mon prochain scénario consisterait à lire les données de deux tables: une table Student et une table Address.Impossible de faire fonctionner le multi-mapping dans Dapper

Table Student a une clé primaire de StudentID (INT IDENTITY), Address a un AddressID (INT IDENTITY). Student a également un FK appelé AddressID reliant dans la table Address.

Mon idée était de créer deux classes, une pour chaque table, avec les propriétés qui me intéressent. De plus, je mets une propriété PrimaryAddress de type Address sur ma Student classe en C#.

J'ai ensuite essayé de récupérer les données des élèves et adresse dans une seule requête - je miment l'échantillon qui est donnée sur la Github page:

var data = connection.Query<Post, User>(sql, (post, user) => { post.Owner = user; }); 
var post = data.First(); 

Ici, un Post et un User sont récupérés, et le propriétaire de le poste est défini à l'utilisateur - le type renvoyé est un Post - correct?

Donc, dans mon code, je définir deux paramètres à la méthode Query d'extension générique - un Student comme le premier qui doit être retourné, et un Address comme le second, qui sera stocké sur l'instance de l'étudiant:

var student = _conn.Query<Student, Address> 
        ("SELECT s.*, a.* FROM dbo.Student s 
         INNER JOIN dbo.Address a ON s.AddressID = a.AddressID 
         WHERE s.StudentenID = @Id", 
        (stu, adr) => { stu.PrimaryAddress = adr; }, 
        new { Id = 4711 }); 

le problème est - j'obtiens une erreur dans Visual studio:

en utilisant la méthode générique « Dapper.SqlMapper.Query (System.Data.IDbConnection, chaîne , System.Func, dynamique, System.Data.IDbTransaction, bool, string, int ?, System.Data.CommandType?) » Nécessite 6 arguments de type

Je ne comprends pas vraiment pourquoi Dapper insiste sur l'utilisation de cette surcharge avec 6 arguments de type ...

+2

J'ai eu exactement le même problème et il m'a fallu un certain temps pour comprendre pourquoi - comme Sam explique! –

Répondre

21

Ce serait parce que j'ai changé d'API et j'ai oublié de mettre à jour la documentation, j'ai corrigé l'erreur.

Soyez sûr de jeter un oeil à Tests.cs pour une spécification complète et à jour.

En particulier, l'ancienne API utilisait Action<T,U> pour effectuer le mappage, le problème était qu'elle était à la fois arbitraire et rigide. Vous ne pouviez pas contrôler complètement le type de retour. Les nouvelles API prennent un Func<T,U,V>. Vous pouvez donc contrôler le type que vous récupérez depuis le mappeur et il n'est pas nécessaire d'en faire un type mappé.

Je viens ligoté une certaine flexibilité supplémentaire autour de la cartographie à plusieurs, ce test doit préciser:

class Person 
{ 
    public int PersonId { get; set; } 
    public string Name { get; set; } 
} 

class Address 
{ 
    public int AddressId { get; set; } 
    public string Name { get; set; } 
    public int PersonId { get; set; } 
} 

class Extra 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public void TestFlexibleMultiMapping() 
{ 
    var sql = 
@"select 
1 as PersonId, 'bob' as Name, 
2 as AddressId, 'abc street' as Name, 1 as PersonId, 
3 as Id, 'fred' as Name 
"; 
    var personWithAddress = connection.Query<Person, Address, Extra, Tuple<Person, Address,Extra>> 
     (sql, (p,a,e) => Tuple.Create(p, a, e), splitOn: "AddressId,Id").First(); 

    personWithAddress.Item1.PersonId.IsEqualTo(1); 
    personWithAddress.Item1.Name.IsEqualTo("bob"); 
    personWithAddress.Item2.AddressId.IsEqualTo(2); 
    personWithAddress.Item2.Name.IsEqualTo("abc street"); 
    personWithAddress.Item2.PersonId.IsEqualTo(1); 
    personWithAddress.Item3.Id.IsEqualTo(3); 
    personWithAddress.Item3.Name.IsEqualTo("fred"); 

} 

tubes Dapper toutes les API de cartographie multi-par une seule méthode, donc si quelque chose échoue, il finira dans le 6 param.L'autre partie du puzzle était que je ne permettais pas de splits super flexibles, que je viens d'ajouter.

Note, le splitOn param par défaut à Id, ce qui signifie qu'il faudra une colonne appelée id ou Id comme la première limite de l'objet. Cependant, si vous avez besoin de limites sur plusieurs clés primaires qui ont des noms différents pour dire un multi-mapping "3-way", vous pouvez maintenant passer dans une liste séparée par des virgules.

Donc, si nous devions corriger ce qui précède, sans doute les suivantes travailleraient:

var student = _conn.Query<Student,Address,Student> 
       ("SELECT s.*, a.* FROM dbo.Student s 
        INNER JOIN dbo.Address a ON s.AddressID = a.AddressID 
        WHERE s.StudentenID = @Id", 
       (stu, adr) => { stu.PrimaryAddress = adr; return stu;}, 
       new { Id = 4711 }, splitOn: "AddressID").FirstOrDefault(); 
+0

OK, merci - mais comment puis-je ajouter des critères comme 'StudentId = @ Id' (et ensuite définir' @ID = 4711') à cette requête multi-mapping ?? –

+1

@marc_s a édité cela dans ... –

+0

@marc_s gardez à l'esprit qu'il y a une tonne de params optionnels pour contrôler des choses comme le type de commande, le timeout de commande et ainsi de suite. un très important à apprendre est "tamponné" ce qui vous permet d'amortir les résultats pour que SqlReaders ne se chevauche pas –

Questions connexes