Référentiel générique Avec EF 4.1 quel est le point

145

Alors que je creuse plus profondément dans le DbContext, DbSet et les interfaces associées, je me demande pourquoi vous auriez besoin de mettre en œuvre un référentiel «générique» distinct autour de ces implémentations?

Il semble que DbContext et IDbSet font tout ce dont vous avez besoin et incluent l '«unité de travail» dans DbContext.

Est-ce que je manque quelque chose ici ou semble-t-il que les gens aiment ajouter une autre couche de dépendance sans raison.

Code Jammr
la source
C'est une question peu controversée / basée sur l'opinion. J'en ai discuté ici .
Amit Joshi du

Réponses:

202

Vous avez vraiment raison. DbContextest une implémentation du modèle d'unité de travail et IDbSetest une implémentation du modèle de référentiel.

Les référentiels sont actuellement très populaires et surutilisés. Tout le monde les utilise simplement parce qu'il existe des dizaines d'articles sur la création d'un référentiel pour le cadre d'entité, mais personne ne décrit réellement les défis liés à cette décision.

Les principales raisons d'utiliser le référentiel sont généralement:

  • Masquer EF de la couche supérieure
  • Rendre le code plus testable

La première raison est une sorte de pureté architectonique et une excellente idée que si vous rendez vos couches supérieures indépendantes sur EF, vous pouvez plus tard passer à un autre cadre de persistance. Combien de fois avez-vous vu une telle chose dans le monde réel? Cette raison rend le travail avec EF beaucoup plus difficile car votre référentiel doit exposer de nombreuses fonctionnalités supplémentaires enveloppant ce que EF autorise par défaut.

Dans le même temps, l'encapsulation du code EF peut garder votre code mieux organisé et suivre la règle de séparation des préoccupations. Pour moi, cela peut être le seul réel avantage du référentiel et de l'unité de travail, mais vous devez comprendre que suivre cette règle avec EF rendra peut-être votre code mieux maintenable et mieux lisible, mais dans l'effort initial pour créer votre application sera beaucoup plus élevé et pour les petites applications, cela peut être une complexité inutile.

La deuxième raison est partiellement correcte. Le gros inconvénient d'EF est une architecture rigide qui peut difficilement être moquée.Par conséquent, si vous souhaitez effectuer un test unitaire sur la couche supérieure, vous devez en quelque sorte envelopper EF pour permettre de se moquer de son implémentation. Mais cela a de nombreuses autres conséquences que j'ai décrites ici .

Je suis le blog d'Ayende . Si vous avez déjà utilisé NHibernate, vous connaissez probablement ses articles. Ce type a récemment écrit plusieurs articles contre l'utilisation du référentiel avec NHibernate, mais NHibernate est beaucoup mieux mockable.

Ladislav Mrnka
la source
3
Vous pouvez IDbSetvous moquer de vous pouvez également définir une interface personnalisée dans votre contexte dérivé, mais c'est tout. Une fois que votre code utilise ChangeTracker, Entries ou quoi que ce soit d'autre, il faudra beaucoup d'efforts pour les envelopper tous.
Ladislav Mrnka
1
Oui, EF n'est pas un outil très axé sur les performances. Au moins MS a beaucoup d'opportunités pour améliorer cela dans les versions futures.
Ladislav Mrnka
2
@chiccodoro: C'est vrai. Mais une fois que votre classe simulée expose IQueryableou accepte Expression<>comme paramètre qui est mis en interne dans la requête Linq-to-entity, vous définissez une logique en dehors du composant simulé avec des effets secondaires qui ne peuvent pas être testés avec des tests unitaires.
Ladislav Mrnka
8
Si j'utilise DbSet et BdContext directement dans ma couche métier, je dois y référencer EntityFramework.dll ainsi que dans mon projet DataLayer. Cela seul me dit qu'il a besoin d'une sorte d'emballage.
Ingó Vals
2
downvote: incomplet - l'abstraction d'EF derrière une interface de référentiel peut exécuter exactement le même code client dans SL et WPF.
h.alex
21

Je suis aux prises avec les mêmes problèmes, et la mockability pour les tests unitaires des couches EF est importante. Mais j'ai parcouru cet excellent article qui explique comment configurer EF 4.1 DbContext pour qu'il soit mockable en m'assurant que votre DbContext dérivé implémentait une interface générique et expose IDbSet plutôt que DbSet. Puisque j'utilise une approche Database First, parce que notre base de données existe déjà, j'ai simplement modifié les modèles T4 utilisés pour générer mon DbContext dérivé pour le générer pour renvoyer les interfaces IDbSet, ainsi que pour dériver de mon interface générique. De cette façon, tout peut être facilement moqué et vous n'avez pas besoin d'implémenter votre propre modèle d'unité de travail ou de référentiel. Écrivez simplement votre code de service pour consommer votre interface générique, et lorsque vous passez au test unitaire,

http://refactorthis.wordpress.com/2011/05/31/mock-faking-dbcontext-in-entity-framework-4-1-with-a-generic-repository/

Kendall Bennett
la source
5

L'une des raisons de la création du référentiel est que vous pouvez masquer l'implémentation de DBSet et DbContext si vous décidez de passer d'EntityFramework à autre chose ou vice versa.

Par exemple, j'utilisais NHibernate et j'ai encapsulé tous les appels à ce framework dans mes classes de référentiel. Ils renvoient IEnumerable pour qu'ils soient "génériques" et mes référentiels ont les opérations CRUD standard (mise à jour, suppression, etc.). Je suis depuis longtemps passé à Entity Framework. Ce faisant, je n'ai pas eu besoin de changer quoi que ce soit dans mes classes ViewModel ou au-delà, car elles pointaient vers mon référentiel - je n'avais besoin que de modifier l'intérieur de mon référentiel. Cela a rendu la vie beaucoup plus facile lors de la migration.

(J'ai utilisé NHibernate parce que nous nous connectons à l'ISeries, et à l'époque, il n'y avait aucune implémentation économique utilisant EF avec l'ISeries. La seule disponible était de payer 12 000 $ à IBM pour leur DB2Connect)

Leniel Maccaferri
la source
"Presque" (en ce qui concerne le masquage de DBSet et DbContext), vous constaterez que vous n'avez pas besoin d'exposer EF à des consommateurs (par exemple si vous utilisez DI) mais vous avez besoin d'une interface qui expose les propriétés IDbSet <T> ou allez un peu plus loin et tapez à la place toutes vos propriétés comme IQueryable <T>, mais mon point est que vous pouvez complètement masquer votre dépendance à DbSet et DbContext. Les opérations CRUD peuvent ensuite être écrites en tant que méthodes d'extension, vous pouvez écrire plusieurs méthodes d'extension pour différents magasins de sauvegarde. Cependant, vous ne cacheriez pas l'utilisation de LINQ.
Shaun Wilson