web-dev-qa-db-fra.com

Qu'est-ce qu'un dépôt IR et à quoi sert-il?

Qu'est-ce qu'un IRepository? Pourquoi est-il utilisé, des exemples brefs et simples ne feront pas de mal.

37
Sevki

MVC favorise la séparation des préoccupations, mais cela ne s'arrête pas au niveau M V C.

L'accès aux données est une préoccupation en soi. Cela devrait être fait dans le bit M de MVC, c'est-à-dire le modèle. La façon dont vous structurez votre modèle dépend de vous, mais les gens suivent généralement des modèles éprouvés (pourquoi réinventer la roue?). Le modèle de référentiel est la norme actuelle. Ne vous attendez cependant pas à une formule simple, car les variations sont presque aussi nombreuses qu'il y a de développeurs.

IRepository est juste une interface que vous créez (elle ne fait pas partie de MVC ou ASP.NET ou .NET). Il vous permet de "découpler" vos référentiels des implémentations réelles. Le découplage est bon car cela signifie que votre code ...:

  1. Votre code est beaucoup plus réutilisable. C'est tout simplement bon.
  2. Votre code peut utiliser l'inversion de contrôle (ou l'injection de dépendance). C'est bon de bien séparer vos préoccupations. C'est particulièrement bon car cela permet des tests unitaires ...
  3. Votre code peut être testé à l'unité. C'est particulièrement bon dans les grands projets avec des algorithmes complexes. Il est bon partout car il améliore votre compréhension des technologies avec lesquelles vous travaillez et des domaines que vous essayez de modéliser dans un logiciel.
  4. Votre code est construit autour des meilleures pratiques, suivant un modèle commun. C'est bien car cela rend la maintenance beaucoup plus facile.

Donc, après vous avoir vendu le découplage, la réponse à votre question est que IRepository est une interface que vous créez et dont vous faites hériter vos référentiels. Il vous donne une hiérarchie de classes fiable avec laquelle travailler.

J'utilise généralement un IRepository générique:

IRepository

Où TEntity est, bien, une entité. Le code que j'utilise est:

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

namespace Wingspan.Web.Mvc
{
    public interface IRepository<TEntity> where TEntity : class
    {
        List<TEntity> FetchAll();
        IQueryable<TEntity> Query {get;}
        void Add(TEntity entity);
        void Delete(TEntity entity);
        void Save();
    }
}

Une implémentation concrète de cette interface serait:

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

using Wingspan.Web.Mvc;

namespace ES.eLearning.Domain
{
    public class SqlRepository<T> : IRepository<T> where T : class
    {
        DataContext db;
        public SqlRepository(DataContext db)
        {
            this.db = db;
        }

        #region IRepository<T> Members

        public IQueryable<T> Query
        {
            get { return db.GetTable<T>(); }
        }

        public List<T> FetchAll()
        {
            return Query.ToList();
        }

        public void Add(T entity)
        {
            db.GetTable<T>().InsertOnSubmit(entity);
        }

        public void Delete(T entity)
        {
            db.GetTable<T>().DeleteOnSubmit(entity);
        }

        public void Save()
        {
            db.SubmitChanges();
        }

        #endregion
    }
}

Cela me permet d'écrire:

SqlRepository<UserCourse> UserCoursesRepository = new SqlRepository<UserCourse>(db);

Où db est une instance DataContext injectée, disons, dans un service.

Avec UserCoursesRepository, je peux maintenant écrire des méthodes dans ma classe Service comme:

public void DeleteUserCourse(int courseId)
        {
            var uc = (UserCoursesRepository.Query.Where(x => x.IdUser == UserId && x.IdCourse == courseId)).Single();
            UserCoursesRepository.Delete(uc);
            UserCoursesRepository.Save();
        }

Et maintenant, dans mes contrôleurs, je peux simplement écrire:

MyService.DeleteUserCourse(5);
MyService.Save();

Avec ce modèle, le développement de votre application devient plus une ligne d'assemblage qui mène à un contrôleur TRÈS simple. Chaque élément de la ligne d'assemblage peut être testé indépendamment de tout le reste, de sorte que les bogues sont éliminés dans l'œuf.

S'il s'agit d'une réponse longue et compliquée, c'est parce que la vraie réponse est:

Achetez le livre de Steven Sanderson Pro ASP.NET MVC 2 Framework et apprenez à penser en MVC.

49
awrigley

Un IRepository est une interface que vous spécifiez lorsque vous souhaitez implémenter le modèle de référentiel. Comme l'a déclaré @Brian Ball, cela ne fait pas partie de .NET, c'est une interface que vous créez.

Les développeurs utilisant le modèle de référentiel recommandent largement l'utilisation d'une interface pour l'implémentation. Par exemple, dans l'application que je développe actuellement, j'ai 5 référentiels. 4 spécifiques et 1 générique. Chacun hérite d'un IRepository qui garantit que je n'aurai pas de problèmes avec des différences d'implémentation.

En ce qui concerne les exemples de code, je vais essayer:

interface IRepository<T> where T : class {
    IQueryable<T> Select();
}

Implémenté en tant que référentiel générique:

public class Repository<T> : IRepository<T> where T : class {
    public IQueryable<T> Select() {
        return this.ObjectContext.CreateObjectSet<T>();
    }
}

Mis en œuvre en tant que référentiel spécialisé:

public class EmployeeRepository : IRepository<Employee> {
    public IQueryable<Employee> Select() {
        return this.ObjectContext.Employees;
    }
}

Les Repository<T> et EmployeeRepository implémentent IRepository, mais ils effectuent les requêtes légèrement différemment. Le référentiel générique doit créer un ensemble d'objets de T avant de pouvoir essayer de faire quoi que ce soit.

Garde en tête que Repository<T> est censé être verrouillé sur l'interface, où EmployeeRepository peut implémenter des méthodes plus spécialisées pour accomplir une logique plus complexe.

J'espère que cela vous aide un peu.

15
Gup3rSuR4c

IRepository n'est pas un type défini dans le framework .Net. Habituellement, lorsque vous voyez une interface nommée cela, le programme utilise le modèle de référentiel ( https://web.archive.org/web/20110503184234/http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/ 10/08/the-repository-pattern.aspx ). Généralement, lorsque les utilisateurs utilisent ce modèle, ils créent une interface à laquelle adhèrent tous les référentiels. Cela présente de nombreux avantages. Certains des avantages sont le découplage du code et les tests unitaires.

Il est également courant que cela soit fait afin qu'il puisse être utilisé avec IoC ( http://en.wikipedia.org/wiki/Inversion_of_control ).

4
Brian Ball

Un référentiel est ne abstraction qui représente tout magasin de données sous-jacent et arbitraire comme s'il s'agissait d'une collection d'objets en mémoire.

Cette définition est transformée en une forme plus pratique en raison des pratiques courantes et des limitations du système comme ne collection d'objets en mémoire qui représentent un magasin de données sous-jacent et arbitraire, éventuellement déconnecté. Sous le capot, le référentiel peut être lié à une base de données, un fichier plat, une collection d'objets en mémoire ou tout ce que vous pouvez imaginer. L'utilisateur d'un référentiel s'en fiche.

Un IRepository est donc le contrat d'interface qui définit comment le code Api souhaite que le code client interagisse avec le référentiel. Cela inclut souvent l'ajout, la mise à jour, la suppression et l'obtention de contrats, comme par exemple, cet exemple très courant de contrat de référentiel:

public interface IRepository<TEntity> where TEntity : class
{
    List<TEntity> GetAll();
    void Add(TEntity entity);
    void Delete(TEntity entity);
    void Save();
}

Mais je préfère utiliser une interface différente pour plusieurs raisons.

Tout d'abord, vous n'utiliserez généralement pas un référentiel seul, vous l'utiliserez probablement avec un modèle d'unité de travail, donc le référentiel ne devrait pas avoir de méthode Save (). Il pourrait avoir une méthode Update(T entity) - mais pourquoi? L'objet que vous recevez du référentiel sera automatiquement actualisable/mis à jour comme tout autre objet que vous recevriez de tout type de collection d'objets car vous avez récupéré des références aux objets eux-mêmes. (Par exemple: si votre TEntity est un objet Person, et que vous obtenez la personne "Chuck", et que vous changez son nom de famille de "Bartowski" en "Carmichael", le référentiel a probablement déjà mis à jour ladite entité. Si cela semble ténu dans votre esprit, il n'y a rien de mal à implémenter une méthode Update(T entity).)

Deuxièmement, la plupart des référentiels devraient être capables de gérer des environnements déconnectés. Si votre solution n'a pas cette exigence, vous pouvez toujours créer une interface qui gère les scénarios déconnectés et la laisser simplement non implémentée. Vous êtes maintenant prêt pour l'avenir.

Enfin, notre contrat donne plus de sens à la vraie nature d'un référentiel - ne collection d'objets en mémoire qui représentent un magasin de données arbitraire, éventuellement déconnecté.

public interface IRepository<TEntity> where TEntity : class
{

    List<TEntity> GetAll();
    List<TEntity> Get(Func<TEntity, bool> where);
    void Insert(TEntity entity);
    void Insert(IEnumerable<TEntity> entities);
    void Remove(TEntity entity);
    void Remove(IEnumerable<TEntity> entities);

    void SyncDisconnected(TEntity entity, bool forDeletion = false);
    void SyncDisconnected(IEnumerable<TEntity> entities, bool forDeletion = false);
}

Si vous définissez une classe de base pour toutes vos entités, appelons-la DomainObject et que vous lui donnez un champ Id, vous pouvez effectuer les opérations suivantes:

public interface IRepository<TEntity> where TEntity : DomainObject
{
    TEntity GetById(object Id);

    List<TEntity> GetAll();
    List<TEntity> Get(Func<TEntity, bool> where);
    void Insert(TEntity entity);
    void Insert(IEnumerable<TEntity> entities);
    void Remove(TEntity entity);
    void Remove(IEnumerable<TEntity> entities);

    void SyncDisconnected(TEntity entity, bool forDeletion = false);
    void SyncDisconnected(IEnumerable<TEntity> entities, bool forDeletion = false);
}

Si vous n'aimez pas le paramètre facultatif forDeletion, vous pouvez également ajouter une méthode qui permet de synchroniser les objets supprimés:

    void SyncDisconnectedForDeletion(TEntity entity);

La raison pour laquelle vous devez le faire est que, dans la plupart des cas, la synchronisation des objets déconnectés pour suppression est incompatible avec la synchronisation des objets déconnectés pour ajout ou modification. (Essayez-le. Vous verrez par vous-même que les exigences de suppression par rapport à un magasin varient énormément de celles d'ajout ou de modification). Par conséquent, l'interface doit définir un contrat afin que l'implémentation puisse discerner entre les deux.

Vous pouvez implémenter cette interface par rapport à TOUT référentiel de N'IMPORTE QUEL magasin de données sous-jacent, connecté ou déconnecté, y compris d'autres abstractions aux magasins de données sous-jacents tels que Entity Framework.

2
Price Jones