Comment faire une insertion et une mise à jour pour un objet avec une propriété de navigation à l'aide de Dapper.Rainbow (ou éventuellement à l'aide de Dapper.Contrib)

dapper dapper-rainbow micro-orm orm

Question

J'ai commencé à regarder Dapper récemment. Je le teste et je suis capable de faire de la base avec le CRUD et ce que je voulais dire par basic est de travailler sur une classe avec cette structure:

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

Maintenant, je cherchais quelque chose qui faciliterait les insertions et les mises à jour et trouverait Dapper.Rainbow. Je l'ai vérifié et j'ai pu l'utiliser pour obtenir et insérer des objets comme décrit ci-dessus. Mon problème est que lorsque le Product a une propriété de navigation, je ne peux pas insérer d’insertion sur ce champ. Donc, si j'ai ceci:

public class Product {
    public int Id {get;set;}
    public string Name {get;set;}
    public ProductCategory Category {get;set;}
}

Je ne pourrai pas faire ça:

// connection is a valid and opened connection            
 var db = TestDatabase.Init(connection , 300);
 var newId = db.Products.Insert(newProduct);

pour cette raison:

The member Category of type ProductCategory  cannot be used as a parameter value

Le problème peut être résolu si je remplace Category par le type int (le même type de données dans la base de données). Cependant, si je le fais, je ne pourrai pas interroger un produit avec ses informations de catégorie, plus que l’ID (catégorie).

Donc, sans recourir à Dapper brut, comment puis-je effectuer une insertion et une mise à jour en utilisant une classe avec une propriété de navigation? J'espérais pouvoir faire ce qui suit et dire à Dapper.Rainbow d'ignorer la Category lors de l'insertion ou de la mise à jour.

public class Product {
    public int Id {get;set;}
    public string Name {get;set;}
    public ProductCategory Category {get;set;}
    public int CategoryId {get;set;} // this will be the same field name in the database
}

Ce scénario est possible avec NHibernate où je peux avoir un objet proxy de Category et l'assigner à Product et l'enregistrer et le mappage fonctionne parfaitement. Mais j'adorerais utiliser Dapper et c'est pourquoi j'explore et je veux apprendre comment des choses comme ça peuvent être faites.

Réponse acceptée

Pas avec Dapper.Rainbow

Ce n'est pas possible avec Dapper.Rainbow dans sa forme actuelle, mais ma requête de tirage dans github rend cela possible.

Je suis surpris que personne ne suggère d'utiliser Dapper.Contrib . Je sais que j'ai demandé si la fonctionnalité est dans Rainbow. Mais je ne pensais pas que personne ne remarquera cette déclaration (en particulier le texte en gras):

Maintenant, je cherchais quelque chose qui faciliterait les insertions et les mises à jour et trouverait Dapper.Rainbow. Je l'ai vérifié et j'ai pu l'utiliser pour obtenir et insérer des objets comme décrit ci-dessus. Mon problème est que lorsque le produit a une propriété de navigation, je ne peux pas insérer d’insertion sur ce champ .

... et suggérer une alternative, une solution déjà présente dans la bibliothèque Dapper. Je suppose que j'aurais dû être plus clair avec ma question et demandé explicitement si une solution existe quelque part dans la bibliothèque Dapper entière qui se trouve dans github . Donc, après plus de recherches dans la bibliothèque, j'ai découvert qu'il y avait un support pour mon problème.

Le chemin vers Dapper.Contrib

Tout fonctionne bien avec mon projet et Rainbow jusqu'à ce que j'en ai besoin de plus. J'ai des tables qui contiennent beaucoup de champs. Si je ne fais que nourrir Rainbow mon objet, alors il fera une mise à jour avec tous les champs, ce n'est pas tout bon. Mais cela ne me fait pas sauter rapidement du bateau et retourner à NH. Donc, avant que je mette en place mon propre change tracking et que je ne veuille pas réinventer la roue, surtout si quelqu'un a déjà fait du bon travail, j'ai cherché sur Google et trouvé ce thread SO . Ce fil de discussion a confirmé mes connaissances sur le fait que Rainbow ne supporte pas le suivi des modifications, mais il existe une autre bête qui s'appelle Dapper.Contrib . Et j'ai donc commencé à l'expérimenter.

Et donc on se retrouve

La catégorie membre de type ProductCategory ne peut pas être utilisée comme valeur de paramètre

J'ai eu le même problème qu'avec Rainbow . Contrib ne prend pas en charge la propriété de navigation !? Je commence à sentir que je perds mon temps avec Dapper , et la performance qu’elle offre, pour laquelle je suis après, ne sera que des voeux pieux. Jusqu'à...

The WriteAttribute, est venu à la rescousse ...

Cette classe réside dans le fichier SqlMapperExtensions.cs inclus dans le projet Dapper.Contrib . Je n'ai trouvé aucune documentation sur ce cours et il n'y a aucun commentaire qui puisse le rendre facile à trouver et crier sur moi et dire hey I'm the one you're looking for . Je suis tombé dessus quand j'ai mis de côté Rainbow comme je l'ai décrit ci-dessus.

L'utilisation de cette classe est identique à celle que j'ai IgnorePropertyAttribute avec IgnorePropertyAttribute , c'est un attribut avec lequel vous pouvez décorer la propriété de votre classe. Vous devez décorer, avec cet attribut, toute propriété que vous ne souhaitez pas inclure dans le sql que Dapper crée. Donc, dans mon exemple, pour que je dise à Dapper d'exclure le champ Category , je devais le faire:

public class Product {
    public int Id {get;set;}

    public string Name {get;set;}

    [Write(false)] // tell Dapper to exclude this field from the sql
    public ProductCategory Category {get;set;}

    public int CategoryId {get;set;}
}

Je suis presque là

N'oubliez pas que la raison pour laquelle je vais pour Contrib est due à la fonctionnalité de change tracking . Ce thread SO , le même lien que ci-dessus, indique que pour que le suivi des modifications intervienne, vous devez disposer d'une interface pour votre classe et l'utiliser avec Contrib . Donc, pour mon exemple, je dois avoir:

public interface IProduct {
    int Id {get;set;}
    string Name {get;set;}
    ProductCategory Category {get;set;}
    int Category {get;set;}
}

// and implement it on my Product class
public class Product : IProduct {
    public int Id {get;set;}

    public string Name {get;set;}

    [Write(false)]
    public ProductCategory Category {get;set;}

    int Category {get;set;}
}

Je pensais que c'était ça, presque! Vous vous demandez peut-être pourquoi je devrais définir Category dans mon interface si Dapper ne s'en soucie pas du tout. En fait, cela ne ferait que causer un problème, un problème que je résoudrais alors.

Dans mon scénario particulier, je dois parfois travailler sur le champ Category tout en conservant le suivi des modifications pour l'objet Product . Pour maintenir la capacité de suivi, l'appel get doit être alimenté par un type d'interface comme celui-ci:

var product = connection.Get<IProduct>(id);

et avec cet appel je ne pourrais pas accéder au champ Category si je ne le définirai pas dans mon interface. Mais si je le définis dans mon interface, j'aurais alors l'erreur familière

Le membre {membre} de type {type} ne peut pas être utilisé comme valeur de paramètre.

Vraiment encore? Faites ceci s'il vous plait.

Le verdict

Pas besoin de s'inquiéter car celui-ci est facile à résoudre en décorant le membre d'interface comme nous l'avons fait pour la classe. Donc, la configuration finale pour que tout fonctionne est:

public interface IProduct {
    // I will not discuss here what this attribute does
    // as this is documented already in the github source.
    // Just take note that this is needed,
    // both here and in the implementing class.
    [Key]
    int Id {get;set;}

    string Name {get;set;}

    [Write(false)]
    ProductCategory Category {get;set;}

    int Category {get;set;}
}

// and implement it on my Product class
public class Product : IProduct {
    [Key]        
    public int Id {get;set;}

    public string Name {get;set;}

    [Write(false)]
    public ProductCategory Category {get;set;}

    int Category {get;set;}
}

Vous pouvez utiliser cette approche si vous préférez travailler avec Contrib qui dispose de la fonctionnalité de change tracking . Si vous voulez travailler avec Rainbow et que vous rencontrez des problèmes avec les propriétés de la navigation, vous pouvez jouer avec ma requête de tirage . Il fonctionne de la même manière que WriteAttribute mais WriteAttribute uniquement avec Rainbow .

Si vous n'êtes pas fan de décorer vos classes avec des attributs, les deux projets d'extension ne sont pas pour vous. Je sais qu'il existe un autre projet d'extension qui vous permettra de faire une configuration de type courant, mais cela ne va pas avec la bibliothèque Dapper qui se trouve dans github. Ma préférence, qui est de ne travailler qu'avec la bibliothèque principale, me pousse à étudier toute la bibliothèque et à voir si les choses sont déjà là ou si elles peuvent être améliorées pour répondre à mes besoins. Et c'est ce que j'ai fait et expliqué ici, à la fois pour Rainbow et Contrib .

J'espère que cette contribution, la classe très simple que j'ai ajoutée, les astuces de configuration que j'ai montrées et le scénario qui les a amenés, aideront une personne qui souhaite utiliser Dapper, et qui aura une configuration similaire à celle que je possède. En outre, cette réponse sensibilisera davantage les développeurs à ce que Dapper peut et ne peut pas faire. Ce superbe outil appelé Dapper mérite un meilleur wiki et j'espère que cette réponse / cet article ici dans SO aide même dans une petite mesure.


** Et si ce que j'ai écrit ici est déjà écrit quelque part, que je n'ai pas trouvé dans les deux semaines que j'attendais une réponse, alors je serai heureux que quelqu'un m'y relie. Cela fait deux semaines maintenant et les 29 personnes qui se sont penchées sur ma question n'ont pas suggéré de liens ou de solutions, alors j'ai supposé que les informations que je partageais ici étaient nouvelles pour Dapper * :)

REMARQUE: J'ai modifié le titre de la question pour que les autres puissent voir cette solution potentielle à leur problème. Le nouveau titre est basé sur les nouvelles connaissances acquises sur Dapper.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi