J'essaie d'utiliser la fonctionnalité Multimapping de dapper pour renvoyer une liste de ProductItems et de clients associés.
[Table("Product")]
public class ProductItem
{
public decimal ProductID { get; set; }
public string ProductName { get; set; }
public string AccountOpened { get; set; }
public Customer Customer { get; set; }
}
public class Customer
{
public decimal CustomerId { get; set; }
public string CustomerName { get; set; }
}
Mon code dapper est le suivant
var sql = @"select * from Product p
inner join Customer c on p.CustomerId = c.CustomerId
order by p.ProductName";
var data = con.Query<ProductItem, Customer, ProductItem>(
sql,
(productItem, customer) => {
productItem.Customer = customer;
return productItem;
},
splitOn: "CustomerId,CustomerName"
);
Cela fonctionne bien mais il semble que je doive ajouter la liste complète des colonnes au paramètre splitOn pour renvoyer toutes les propriétés des clients. Si je n'ajoute pas "CustomerName", il renvoie null. Est-ce que je ne comprends pas la fonctionnalité de base de la fonction de multi-cartographie. Je ne veux pas avoir à ajouter une liste complète des noms de colonnes à chaque fois.
Réponses:
Je viens de lancer un test qui fonctionne bien:
Le paramètre splitOn doit être spécifié comme point de partage, il vaut par défaut Id. S'il y a plusieurs points de partage, vous devrez les ajouter dans une liste délimitée par des virgules.
Dites que votre jeu d'enregistrements ressemble à ceci:
Dapper a besoin de savoir comment diviser les colonnes dans cet ordre en 2 objets. Un coup d'œil rapide montre que le client commence par la colonne
CustomerId
, par conséquentsplitOn: CustomerId
.Il y a une grande mise en garde ici, si l'ordre des colonnes dans la table sous-jacente est inversé pour une raison quelconque:
splitOn: CustomerId
entraînera un nom de client nul.Si vous spécifiez
CustomerId,CustomerName
comme points de partage, dapper suppose que vous essayez de diviser le jeu de résultats en 3 objets. Le premier commence au début, le deuxième commence àCustomerId
, le troisième àCustomerName
.la source
spliton
, c'est-à-direCustomerId,CustomerName
nonCustomerId, CustomerName
, car Dapper ne fait pasTrim
les résultats de la division de chaîne. Il lancera simplement l'erreur générique de spliton. M'a rendu fou un jour.Nos tables sont nommées de la même manière que la vôtre, où quelque chose comme "CustomerID" peut être renvoyé deux fois en utilisant une opération 'select *'. Par conséquent, Dapper fait son travail mais se divise juste trop tôt (peut-être), car les colonnes seraient:
Cela rend le paramètre spliton: pas si utile, surtout lorsque vous ne savez pas dans quel ordre les colonnes sont retournées. Bien sûr, vous pouvez spécifier manuellement les colonnes ... mais nous sommes en 2017 et nous le faisons rarement plus pour les objets de base.
Ce que nous faisons, et cela a très bien fonctionné pour des milliers de requêtes pendant de nombreuses années, est simplement d'utiliser un alias pour Id, et de ne jamais spécifier spliton (en utilisant l'ID par défaut de Dapper).
... voila! Dapper ne se divisera que sur l'ID par défaut, et cet ID apparaît avant toutes les colonnes Client. Bien sûr, cela ajoutera une colonne supplémentaire à votre jeu de résultats de retour, mais cela représente une surcharge extrêmement minime pour l'utilité supplémentaire de savoir exactement quelles colonnes appartiennent à quel objet. Et vous pouvez facilement développer cela. Besoin d'informations sur l'adresse et le pays?
Mieux encore, vous montrez clairement dans un minimum de sql quelles colonnes sont associées à quel objet. Dapper fait le reste.
la source
En supposant la structure suivante où '|' est le point de division et Ts sont les entités auxquelles le mappage doit être appliqué.
Voici la requête pimpante que vous devrez écrire.
Nous voulons donc que TFirst mappe col_1 col_2 col_3, pour TSecond le col_n col_m ...
L'expression splitOn se traduit par:
Commencez le mappage de toutes les colonnes dans TFrist jusqu'à ce que vous trouviez une colonne nommée ou aliasée comme «col_3», et incluez également «col_3» dans le résultat du mappage.
Ensuite, commencez à mapper dans TSecond toutes les colonnes à partir de 'col_n' et continuez à mapper jusqu'à ce qu'un nouveau séparateur soit trouvé, qui dans ce cas est 'col_A' et marque le début du mappage TThird et ainsi d'un.
Les colonnes de la requête sql et les accessoires de l'objet de mappage sont dans une relation 1: 1 (ce qui signifie qu'ils doivent être nommés de la même manière), si les noms de colonnes résultant de la requête SQL sont différents, vous pouvez les alias en utilisant le 'AS [ Expression Some_Alias_Name].
la source
Il y a une autre mise en garde. Si le champ CustomerId est nul (généralement dans les requêtes avec jointure gauche), Dapper crée ProductItem avec Customer = null. Dans l'exemple ci-dessus:
Et même un autre avertissement / piège. Si vous ne mappez pas le champ spécifié dans splitOn et que ce champ contient null, Dapper crée et remplit l'objet associé (Customer dans ce cas). Pour démontrer l'utilisation de cette classe avec SQL précédent:
la source
Je fais cela de manière générique dans mon repo, cela fonctionne bien pour mon cas d'utilisation. Je pensais partager. Peut-être que quelqu'un étendra cela plus loin.
Certains inconvénients sont:
Le code:
la source
Si vous devez mapper une grande entité, écrire chaque champ doit être une tâche difficile.
J'ai essayé la réponse @BlackjacketMack, mais l'une de mes tables a une colonne Id d'autres non (je sais que c'est un problème de conception de base de données, mais ...) alors cela insère un fractionnement supplémentaire sur dapper, c'est pourquoi
Ça ne marche pas pour moi. Ensuite, j'ai terminé avec un petit changement à ceci, insérez simplement un point de partage avec un nom qui ne correspond à aucun champ sur les tables, en cas de changement
as Id
paras _SplitPoint_
, le script sql final ressemble à ceci:Ensuite, dans dapper, ajoutez juste un splitOn comme ceci
la source