J'ai lu récemment qu'il n'est pas recommandé d'utiliser le modèle de référentiel conjointement avec un ORM. D'après ma compréhension, c'est parce que l'abstraction qu'ils fournissent sur la base de données SQL est trop fuyante pour être contenue par le modèle.
J'ai quelques questions à ce sujet:
Que faites-vous si vous souhaitez désactiver les ORM? Vous auriez du code spécifique ORM dans votre application si vous ne le contenez pas dans un référentiel.
Le modèle de référentiel est-il toujours valide lorsque vous n'utilisez pas d'ORM et vous utilisez ADO.NET pour l'accès aux données et le remplissage des données d'objet vous-même?
Si vous utilisez un ORM mais pas le modèle de référentiel, où conservez-vous les requêtes couramment utilisées? Serait-il sage de représenter chaque requête en tant que classe et d'avoir une sorte de fabrique de requêtes pour créer des instances?
la source
Réponses:
1) Vrai, mais à quelle fréquence changez-vous un ORM?
2) Je dirais que oui, car un contexte ORM est une sorte de référentiel et cache beaucoup de travail impliquant la création de requêtes, la récupération de données, le mappage, etc. Si vous n'utilisez pas d'ORM, cette logique doit encore résider quelque part. Je ne sais pas si cela pourrait être qualifié de modèle de référentiel au sens strict du terme ...
3) L'encapsulation des requêtes est quelque chose que je vois fréquemment, mais c'est généralement plus à des fins de test / stubbing. En dehors de cela, je serais prudent lors de la réutilisation d'une requête dans différentes parties d'une application, car vous risquez alors de créer une dépendance sur quelque chose qui pourrait changer n fois (n étant le nombre d'emplacements où vous utilisez la requête).
la source
Je n'ai pas encore été dans une position où l'entreprise a soudainement décidé de changer de technologie d'accès aux données. Si cela se produit, des travaux seront nécessaires. J'ai tendance à abstraire les opérations d'accès aux données via des interfaces. Le référentiel est un moyen de résoudre ce problème.
J'aurais alors un assemblage différent pour la mise en œuvre concrète de ma couche d'accès aux données. Par exemple, je pourrais avoir:
Company.Product.Data
etCompany.Product.Data.EntityFramework
assemblages. Le premier assemblage serait utilisé uniquement pour les interfaces, alors qu'un autre serait une implémentation concrète de la logique d'accès aux données d'Entity Framework.Je pense que c'est à vous de décider quel modèle est valide ou non. J'ai utilisé un modèle de référentiel dans la couche de présentation. Une chose à garder à l'esprit est que les gens aiment jeter des responsabilités dans des référentiels. Avant de vous en rendre compte, votre classe de référentiel dansera, chantera et fera toutes sortes de choses. Vous voulez éviter cela.
J'ai vu une classe de référentiel qui a commencé par avoir des responsabilités GetAll, GetById, Update et Delete, ce qui est bien. Au moment où le projet était terminé, cette même classe avait des dizaines de méthodes (responsabilités) qui n'auraient jamais dû être là. Par exemple GetByForename, GetBySurname, UpdateWithExclusions et toutes sortes de trucs fous.
C'est là que les requêtes et les commandes entrent en jeu.
Je pense que c'est une très bonne idée d'utiliser des requêtes et des commandes au lieu de référentiels. Je fais ce qui suit:
Définissez l'interface pour une requête. Cela vous aidera à effectuer des tests unitaires. Par exemple
public interface IGetProductsByCategoryQuery { ... }
Définissez une implémentation concrète pour une requête. Vous pourrez les injecter via le cadre IoC de votre choix. Par exemple
public class GetProductsByCategoryQuery : IGetProductsByCategoryQuery
Maintenant, au lieu de polluer le référentiel avec des dizaines de responsabilités, je regroupe simplement mes requêtes dans des espaces de noms. Par exemple, une interface pour la requête ci-dessus peut vivre:
Company.SolutionName.Products.Queries
et l'implémentation peut vivre dansCompany.SolutionName.Products.Queries.Implementation
Lorsqu'il s'agit de mettre à jour ou de supprimer des données, j'utilise le modèle de commande de la même manière.
Certains pourraient être en désaccord et dire qu'avant la fin du projet, vous aurez des dizaines de classes et d'espaces de noms. Oui. Dans mon esprit, c'est une bonne chose car vous pouvez parcourir la solution dans l'IDE de votre choix et voir instantanément le type de responsabilités de certains composants. Si vous avez décidé d'utiliser un modèle de référentiel à la place, vous devrez regarder à l'intérieur de chaque classe de référentiel en essayant de déterminer ses responsabilités.
la source
AVERTISSEMENT: Ce qui suit est basé sur ma compréhension et ma brève expérience dudit modèle (référentiel qui est). Je me trompe peut-être ... en fait, je suis assez certain que je me trompe :). Alors, s'il s'agit d'une tentative de réponse, c'est aussi une question déguisée.
J'utilise le modèle de référentiel pour abstraire la couche d'accès aux données, qui est dans la plupart des cas un ORM. Il s'agit d'une interface générique, avec des méthodes pour créer, lire, mettre à jour, supprimer et implémenter des classes pour LINQ to SQL et EF jusqu'à présent. Je pourrais faire une implémentation qui écrit dans des fichiers XML sur disque (juste un exemple sauvage de ce que je pourrais en faire). Je ne mets pas en œuvre l'unité de travail car elle est prise en charge par les ORM. Si besoin est, je pourrais probablement l'implémenter. Je l'aime comme ça parce que, jusqu'à présent, cela m'a donné une belle séparation. Je ne dis pas qu'il n'y a pas de meilleures alternatives.
Pour répondre à vos questions:
Pour vous donner un autre exemple, les gars d'Umbraco ont retiré le conteneur DI, juste au cas où ils pourraient vouloir passer à autre chose.
la source
Si l'ORM offre la flexibilité de LINQ, un chargement rapide, etc. Je ne le cacherais pas derrière une couche supplémentaire.
S'il s'agit de SQL brut (micro ORM), la "méthode par requête" doit être utilisée de toute façon pour atteindre la réutilisabilité, ce qui rend le modèle de référentiel un bon ajustement.
Pourquoi auriez-vous besoin de changer?
Celui qui possède la plupart des fonctionnalités dont vous avez besoin doit être utilisé. Il est possible qu'une nouvelle version d'ormX apporte de nouvelles fonctionnalités et s'avère meilleure que la version actuelle, mais ...
Si vous choisissez de masquer l'orm, vous ne pouvez utiliser que les fonctionnalités que tous les candidats ont en commun.
Par exemple, vous ne pouvez pas utiliser de
Dictionary<string, Entity>
propriétés dans vos entités car ormY ne peut pas les gérer.En supposant que LINQ est utilisé, la majorité du commutateur orm change simplement les références de bibliothèque et les remplace
session.Query<Foo>()
parcontext.Foos
ou similaire jusqu'à ce qu'il compile. Tâche ennuyeuse, mais prend moins de temps que le codage de la couche d'abstraction.Oui. Le code doit être réutilisable et cela signifie mettre le bâtiment sql, la matérialisation des objets, etc. en un seul endroit (classe séparée). Vous pouvez aussi appeler la classe "XRepository" et en extraire une interface.
En supposant que LINQ est utilisé, un wrapper de classe serait exagéré à mon humble avis. Une meilleure façon serait les méthodes d'extension
Tout code qui est utilisé à plusieurs endroits (ou qui a le potentiel) et qui est suffisamment compliqué (sujet aux erreurs), doit être extrait dans un endroit centralisé.
la source