Что такое IRepository и для чего он используется?

Что такое IRepository? Почему это используется, короткие и простые примеры не повредят.

4 ответа

Решение

MVC способствует разделению интересов, но это не останавливается на уровне MVC.

Доступ к данным сам по себе является проблемой. Это должно быть сделано в бите M MVC, то есть в модели. Как вы структурируете свою модель, зависит от вас, но люди обычно следуют проверенным и проверенным схемам (зачем изобретать велосипед?). Шаблон репозитория является текущим стандартом. Однако не ждите простой формулы, потому что вариантов почти столько же, сколько и разработчиков.

IRepository - это просто интерфейс, который вы создаете (он не является частью MVC или ASP.NET или.NET). Это позволяет вам "отделить" ваши репозитории от реальных реализаций. Разделение хорошо, потому что это означает, что ваш код...:

  1. Ваш код гораздо более пригоден для повторного использования. Это просто хорошо.
  2. Ваш код может использовать Inversion of Control (или Inpension Injection). Это хорошо, чтобы ваши проблемы были хорошо разделены. Это особенно хорошо, потому что это позволяет модульное тестирование...
  3. Ваш код может быть модульным тестированием. Это особенно хорошо в больших проектах со сложными алгоритмами. Это хорошо везде, потому что расширяет ваше понимание технологий, с которыми вы работаете, и областей, которые вы пытаетесь смоделировать в программном обеспечении.
  4. Ваш код строится на основе лучших практик, следуя общему шаблону. Это хорошо, потому что это делает обслуживание намного проще.

Итак, после того как вы продали развязку, ответ на ваш вопрос заключается в том, что IRepository - это интерфейс, который вы создаете и который наследует ваши хранилища. Это дает вам надежную иерархию классов для работы.

Я обычно использую универсальный IRepository. То есть:

IRepository

Где Tentity это, ну, сущность. Код, который я использую:

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();
    }
}

Конкретная реализация этого интерфейса будет:

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
    }
}

Это позволяет мне написать:

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

Где db - это экземпляр DataContext, внедренный, скажем, в Сервис.

С UserCoursesRepository теперь я могу писать методы в своем классе Service, например:

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

И теперь в моих контроллерах я могу просто написать:

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

Т.е. разработка вашего приложения становится в большей степени сборочной линией, которая ведет к ОЧЕНЬ простому контроллеру. Каждый кусочек сборочной линии может быть протестирован независимо от всего остального, поэтому ошибки будут устранены.

Если это длинный, громоздкий ответ, то это потому, что реальный ответ:

Купите книгу Стивена Сандерсона Pro ASP.NET MVC 2 Framework и научитесь думать в MVC.

IRepository это интерфейс, который вы указываете, когда хотите реализовать шаблон репозитория. Как сказал @Brian Ball, это не часть.NET, это интерфейс, который вы создаете.

Разработчики, использующие шаблон репозитория, широко рекомендуют использовать интерфейс для реализации. Например, в приложении, которое я сейчас разрабатываю, у меня есть 5 репозиториев. 4 конкретных и 1 общий. Каждый наследует от IRepository что гарантирует, что у меня не будет проблем в будущем с различиями в реализации.

Что касается примеров кода, я попробую:

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

Реализовано как общий репозиторий:

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

Реализуется как специализированный репозиторий:

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

Оба Repository<T> а также EmployeeRepository воплощать в жизнь IRepositoryОднако они выполняют запрос немного по-другому. Универсальный репозиторий должен создать набор объектов T, прежде чем он сможет что-либо предпринять.

Имейте в виду, что Repository<T> должен быть заблокирован к интерфейсу, где как EmployeeRepository может реализовать более специализированные методы для выполнения более сложной логики.

Надеюсь, это вам немного поможет.

IRepository не определен тип в.Net Framework. Обычно, когда вы видите интерфейс с таким названием, программа использует шаблон репозитория ( https://web.archive.org/web/20110503184234/http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/10/08/the-repository-pattern.aspx). Обычно, когда люди используют этот шаблон, они создают интерфейс, которого придерживаются все репозитории. Есть много преимуществ для этого. Некоторые из преимуществ - это удаление кода и модульное тестирование.

Для этого также характерно использование IoC ( http://en.wikipedia.org/wiki/Inversion_of_control).

Репозиторий - это абстракция, которая представляет любое базовое и произвольное хранилище данных, как если бы это была коллекция объектов в памяти.

Это определение трансформируется в более практичную форму из-за общепринятых практик и системных ограничений как совокупность объектов в памяти, которые представляют собой какое-то базовое и произвольное хранилище данных, возможно, отключенное. Изнутри хранилище может быть связано с базой данных, плоским файлом, коллекцией объектов в памяти или чем-либо еще, что вы можете себе представить. Пользователю хранилища все равно.

Так что IRepository это интерфейсный контракт, который определяет, как Api-код желает, чтобы клиентский код взаимодействовал с хранилищем. Это часто включает в себя добавление, обновление, удаление и получение контрактов, как, например, этот очень распространенный пример контракта с репозиторием:

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

Но я предпочитаю использовать другой интерфейс по нескольким причинам.

Во-первых, вы обычно не будете использовать репозиторий сам по себе, вы, вероятно, будете использовать его с шаблоном единицы работы, поэтому в репозитории не должно быть метода Save(). Это может иметь Update(T entity) метод - но почему? Объект, который вы получаете из репозитория, будет автоматически обновляться / обновляться так же, как любой другой объект, который вы получили бы из любого вида коллекции объектов, потому что вы получили ссылки на сами объекты. (Например: если ваш TEntity это Person объект, и вы получаете человека "Чак", и вы изменяете его фамилию с "Bartowski" на "Carmichael", хранилище предположительно уже обновило указанную сущность. Если это кажется ненадежным, нет ничего плохого в реализации Update(T entity) Метод.)

Во-вторых, большинство репозиториев должны быть в состоянии обрабатывать отключенные среды. Если ваше решение не имеет этого требования, вы все равно можете создать интерфейс, который обрабатывает отключенные сценарии и просто оставить его нереализованным. Теперь вы готовы к будущему.

Наконец, наш контракт имеет больше смысла для истинной природы хранилища - коллекции объектов в памяти, которые представляют собой произвольное хранилище данных, возможно, отключенное.

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);
}

Если вы определяете базовый класс для всех ваших сущностей, давайте назовем его DomainObjectи вы даете ему Id поле, то вы можете сделать следующее:

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);
}

Если вам не нравится необязательный параметр forDeletion, вы можете добавить метод, который также позволяет синхронизировать удаленные объекты:

    void SyncDisconnectedForDeletion(TEntity entity);

Причина, по которой вам нужно это сделать, заключается в том, что в большинстве случаев синхронизация отключенных объектов для удаления несовместима с синхронизацией отключенных объектов для добавления или изменения. (Попробуйте. Вы сами увидите, что требования для удаления в магазине сильно отличаются от требований добавления или изменения). Следовательно, интерфейс должен определять контракт, чтобы реализация могла различать их.

Вы можете реализовать этот интерфейс для ЛЮБОГО репозитория ЛЮБОГО базового хранилища данных, подключенного или отключенного, включая другие абстракции для базовых хранилищ данных, таких как Entity Framework.

Другие вопросы по тегам