Je suis pro-référentiel, même si je me suis éloigné des modèles génériques de référentiel. Au lieu de cela, j'aligne mes référentiels sur la fonction métier qu'ils servent. Les référentiels n'ont pas pour but d'abstraire l'ORM, car je ne m'attends pas à ce que cela change, et en même temps, j'évite de rendre un référentiel trop granulaire. (Ie CRUD) Au lieu de cela, mes référentiels ont deux ou trois objectifs clés:
- Récupération de données
- Création de données
- Suppression définitive
Pour la récupération des données, le référentiel revient toujours IQueryable<TEntity>
. Pour la création de données, il renvoie TEntity. Le référentiel gère mon filtrage de niveau de base tel que l'état "actif" de l'autorisation pour les systèmes qui utilisent des modèles de suppression logicielle et l'état "actuel" pour les systèmes qui utilisent des données historiques. La création de données est uniquement chargée de s'assurer que les références requises sont résolues et associées et que l'entité est configurée et prête à fonctionner.
La mise à jour des données est de la responsabilité de la logique métier travaillant avec les entités en question. Cela peut inclure des choses comme les règles de validation. Je n'essaie pas d'encapsuler cela dans une méthode de référentiel.
La suppression dans la plupart de mes systèmes est une suppression logicielle afin qu'elle relève de la mise à jour des données. (IsActive = false) Dans le cas de suppressions matérielles, ce serait une ligne unique dans le référentiel.
Pourquoi les référentiels? Test-capacité. Bien sûr, DbContext peut être moqué, mais il est plus simple de se moquer d'une classe qui retourneIQueryable<TEntity>
. Ils jouent également bien avec le modèle UoW, personnellement, j'utilise le modèle DbContextScope de Mehdime pour étendre l'unité de travail au niveau que je veux (c'est-à-dire les contrôleurs dans MVC) et laisser mes contrôleurs et les classes de service d'assistance utiliser les référentiels sans avoir besoin de passer des références à le UoW / dbContext autour. L'utilisation d'IQueryable signifie que vous n'avez pas besoin de beaucoup de méthodes d'encapsulation dans le référentiel, et votre code peut optimiser la façon dont les données vont être consommées. Par exemple, le référentiel n'a pas besoin d'exposer des méthodes telles que "Exists" ou "Count", ou d'essayer d'encapsuler des entités avec d'autres POCO dans les cas où vous souhaitez des sous-ensembles de données. Ils n'ont même pas besoin de gérer les options de chargement rapide pour les données connexes dont vous pouvez ou non avoir besoin. En passant IQueryable, le code appelant peut:
.Any()
.Count()
.Include() // Generally avoided, instead I use .Select()
.Where()
.Select(x => new ViewModel or Anon. Type)
.Skip().Take()
.FirstOrDefault() / .SingleOrDefault() / .ToList()
Très flexible, et à partir d'un PoV de test, mon référentiel simulé doit simplement renvoyer des listes d'objets d'entité remplis.
En ce qui concerne les référentiels génériques, je m'en suis éloigné pour la plupart parce que lorsque vous vous retrouvez avec un référentiel par table, vos contrôleurs / services se retrouvent avec des références à plusieurs référentiels pour effectuer une action commerciale. Dans la plupart des cas, seulement un ou deux de ces référentiels effectuent réellement des opérations d'écriture (à condition que vous utilisiez correctement les propriétés de navigation) tandis que les autres prennent en charge ceux avec des lectures. Je préfère avoir quelque chose comme un référentiel de commandes qui est capable de lire et de créer des commandes, et de lire toutes les recherches pertinentes, etc. (objets clients légers, produits, etc.) pour référence lors de la création d'une commande, plutôt que de toucher 5 ou 6 référentiels différents. Cela peut violer les puristes DNRY, mais mon argument est que le but du référentiel est de servir à la création d'Ordres qui inclut les références associées.Repository<Product>
pour obtenir des produits où pour la base d'une commande j'ai seulement besoin d'une entité avec une poignée de champs. Mon OrderRepository pourrait avoir une .GetProducts()
méthode de retour IQueryable<ProductSummary>
que je trouve plus agréable qu'une Repository<Product>
qui finit par avoir plusieurs méthodes "Get" pour essayer de répondre à différents domaines des besoins de l'application et / ou une expression de filtrage de passage complexe.
J'opte pour un code plus simple, facile à suivre, à tester et à régler. Il peut être abusé de mauvaises manières, mais je préfère avoir quelque chose où les abus sont faciles à repérer et à corriger que d'essayer de "verrouiller" le code d'une manière qui ne peut pas être abusée, échouer, puis avoir quelque chose qui est un cauchemar pour arriver à faire ce que le client paie pour le faire à la fin. :)