web-dev-qa-db-fra.com

Comment utiliser correctement le modèle de référentiel?

Je me demande comment dois-je regrouper mes référentiels? Comme dans les exemples que j'ai vus sur le mvc asp.net et dans mes livres, ils utilisent essentiellement un référentiel par table de base de données. Mais cela semble être un grand nombre de référentiels vous obligeant à appeler de nombreux référentiels plus tard pour des moqueries et d'autres choses.

Je suppose donc que je devrais les regrouper. Cependant, je ne sais pas comment les regrouper.

En ce moment, j'ai créé un référentiel d'enregistrement pour gérer tous mes trucs d'enregistrement. Cependant, il y a comme 4 tables que je dois mettre à jour et avant j'avais 3 référentiels pour le faire.

Par exemple, l'une des tables est une table de licence. Quand ils s'inscrivent, je regarde leur clé et la vérifie pour voir si elle existe dans la base de données. Maintenant, que se passe-t-il si je dois vérifier cette clé de licence ou autre chose de cette table à un autre endroit que l'enregistrement?

Un endroit pourrait être la connexion (vérifiez si la clé n'est pas expirée).

Alors que ferais-je dans cette situation? Réécrire à nouveau le code (pause DRY)? Essayez de fusionner ces 2 référentiels ensemble et espérez qu'aucune des méthodes n'est nécessaire à un autre moment (comme peut-être que je pourrais avoir une méthode qui vérifie si userName est utilisé - peut-être en aurai-je besoin ailleurs).

De plus, si je les fusionne, j'aurais besoin de 2 couches de service allant dans le même référentiel car je pense qu'avoir toute la logique pour 2 parties différentes d'un site serait long et je devrais avoir des noms comme ValidateLogin (), ValdiateRegistrationForm () , ValdiateLoginRetrievePassword () et etc.

Ou appeler le référentiel de toute façon et avoir juste un nom bizarre?

Il semble juste difficile de créer un référentiel qui a un nom assez général pour que vous puissiez l'utiliser pour de nombreux endroits de votre application tout en ayant du sens et je ne pense pas que l'appel à un autre référentiel dans un référentiel serait une bonne pratique.

86
chobo2

Une chose que j'ai mal faite en jouant avec le modèle de référentiel - tout comme vous, je pensais que la table se rapporte au référentiel 1: 1. Lorsque nous appliquons certaines règles de Domain Driven Design - le problème de regroupement des référentiels disparaît souvent.

Le référentiel doit être par racine agrégée et non par table. Cela signifie - si l'entité ne doit pas vivre seule (c'est-à-dire - si vous avez un Registrant qui participe en particulier Registration) - c'est juste une entité, elle n'a pas besoin d'un référentiel, elle devrait être mis à jour/créé/récupéré via le référentiel de la racine agrégée à laquelle il appartient.

Bien sûr - dans de nombreux cas, cette technique de réduction du nombre de référentiels (en fait - c'est plus une technique pour structurer votre modèle de domaine) ne peut pas être appliquée car chaque entité est censée être une racine agrégée (qui dépend fortement de votre domaine, Je ne peux fournir que des suppositions aveugles). Dans votre exemple - License semble être une racine agrégée car vous devez pouvoir les vérifier sans contexte d'entité Registration.

Mais cela ne nous limite pas aux référentiels en cascade (Registration référentiel est autorisé à référencer License référentiel si nécessaire). Cela ne nous limite pas à référencer le référentiel License (préférable - via IoC) directement à partir de l'objet Registration.

Essayez simplement de ne pas conduire votre conception à travers les complications fournies par les technologies ou les malentendus. Regrouper les référentiels dans ServiceX simplement parce que vous ne voulez pas construire 2 référentiels n'est pas une bonne idée.

Beaucoup mieux serait de lui donner un nom propre - RegistrationService i.e.

Mais les services doivent être évités en général - ils sont souvent une cause qui conduit à modèle de domaine anémique .

MODIFIER:
Commencez à utiliser l'IoC. Cela soulage vraiment la douleur d'injecter des dépendances.
Au lieu d'écrire:

var registrationService = new RegistrationService(new RegistrationRepository(),  
      new LicenseRepository(), new GodOnlyKnowsWhatElseThatServiceNeeds());

vous pourrez écrire:

var registrationService = IoC.Resolve<IRegistrationService>();

P.s. Il serait préférable d'utiliser ce qu'on appelle localisateur de service commun mais ce n'est qu'un exemple.

41
Arnis Lapsa

Une chose que j'ai commencé à faire pour résoudre ce problème est de développer des services qui enveloppent N référentiels. Avec un peu de chance, vos frameworks DI ou IoC peuvent aider à rendre cela plus facile.

public class ServiceImpl {
    public ServiceImpl(IRepo1 repo1, IRepo2 repo2...) { }
}

Cela a-t-il du sens? De plus, je comprends que parler des services dans ce manoir peut ou non être conforme aux principes DDD, je le fais simplement parce que cela semble fonctionner.

5
neouser99

Ce que je fais, c'est que j'ai une classe de base abstraite définie comme suit:

public abstract class ReadOnlyRepository<T,V>
{
     V Find(T lookupKey);
}

public abstract class InsertRepository<T>
{
     void Add(T entityToSave);
}

public abstract class UpdateRepository<T,V>
{
     V Update(T entityToUpdate);
}

public abstract class DeleteRepository<T>
{
     void Delete(T entityToDelete);
}

Vous pouvez ensuite dériver votre référentiel de la classe de base abstraite et étendre votre référentiel unique tant que les arguments génériques diffèrent par exemple;

public class RegistrationRepository: ReadOnlyRepository<int, IRegistrationItem>,
                                     ReadOnlyRepository<string, IRegistrationItem> 

etc....

J'ai besoin des référentiels séparés car nous avons des restrictions sur certains de nos référentiels et cela nous donne un maximum de flexibilité. J'espère que cela t'aides.

2
Michael Mann

J'ai ceci comme classe de référentiel et oui, je m'étends dans le référentiel table/zone, mais je dois quand même parfois casser DRY.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MvcRepository
{
    public class Repository<T> : IRepository<T> where T : class
    {
        protected System.Data.Linq.DataContext _dataContextFactory;

        public IQueryable<T> All()
        {
            return GetTable.AsQueryable();
        }

        public IQueryable<T> FindAll(Func<T, bool> exp)
        {
            return GetTable.Where<T>(exp).AsQueryable();
        }

        public T Single(Func<T, bool> exp)
        {
            return GetTable.Single(exp);
        }

        public virtual void MarkForDeletion(T entity)
        {
            _dataContextFactory.GetTable<T>().DeleteOnSubmit(entity);
        }

        public virtual T CreateInstance()
        {
            T entity = Activator.CreateInstance<T>();
            GetTable.InsertOnSubmit(entity);
            return entity;
        }

        public void SaveAll()
        {
            _dataContextFactory.SubmitChanges();
        }

        public Repository(System.Data.Linq.DataContext dataContextFactory)
        {
            _dataContextFactory = dataContextFactory;
        }

        public System.Data.Linq.Table<T> GetTable
        {
            get { return _dataContextFactory.GetTable<T>(); }
        }

    }
}

MODIFIER

public class AdminRepository<T> : Repository<T> where T: class
{
    static AdminDataContext dc = new AdminDataContext(System.Configuration.ConfigurationManager.ConnectionStrings["MY_ConnectionString"].ConnectionString);

    public AdminRepository()
        : base( dc )
    {
    }

J'ai également un datacontext qui a été créé en utilisant la classe Linq2SQL.dbml.

Alors maintenant, j'ai un référentiel standard implémentant des appels standard comme All and Find et dans mon AdminRepository j'ai des appels spécifiques.

Ne répond pas à la question de DRY bien que je ne pense pas.

2
griegs

Le modèle de référentiel est un mauvais modèle de conception. Je travaille avec de nombreux anciens projets .Net et ce modèle provoque généralement des erreurs "Transactions distribuées", "Annulation partielle" et "Pool de connexions épuisé" qui pourraient être évitées. Le problème est que le modèle essaie de gérer les connexions et les transactions en interne mais celles-ci doivent être gérées au niveau de la couche Controller. De plus, EntityFramework résume déjà une grande partie de la logique. Je suggère d'utiliser le modèle Service à la place pour réutiliser le code partagé.

1
ColacX

Voici un exemple d'une implémentation générique du référentiel utilisant FluentNHibernate. Il est capable de conserver n'importe quelle classe pour laquelle vous avez écrit un mappeur. Il est même capable de générer votre base de données à partir des classes de mappage.

1
James Jones

Je vous suggère de regarder Sharp Architecture . Ils suggèrent d'utiliser un référentiel par entité. Je l'utilise actuellement dans mon projet et je suis très satisfait des résultats.

0
Sly