Repository

Ця стаття має кілька недоліків. Будь ласка, допоможіть удосконалити її або обговоріть ці проблеми на сторінці обговорення.
Ця стаття не має інтервікі-посилань. Ви можете допомогти проєкту, знайшовши та додавши їх до відповідного елементу Вікіданих. (15 червня 2019)
Ця стаття містить перелік посилань, але походження тверджень у ній залишається незрозумілим через практично повну відсутність внутрішньотекстових джерел-виносок. Будь ласка, допоможіть поліпшити цю статтю, перетворивши джерела з переліку посилань на джерела-виноски у самому тексті статті. (15 червня 2019)
Вступний розділ цієї статті, ймовірно, несповна підсумовує ключові тези її вмісту. Будь ласка, допоможіть розширити вступ, додавши стислий огляд найважливіших аспектів статті. (15 червня 2019)
UML діаграма, що описує структуру шаблону проєктування Repository

Repository — патерн, який розділяє рівні джерела даних і логіки програми. Часто використовується із патерном Unit Of Work

Переваги та недоліки

Цей розділ має вигляд переліку, який краще подати прозою. Ви можете допомогти викласти список прозою, де це доречно. Ознайомтеся з довідкою з редагування. (15 червня 2019)

Переваги

  • Використовується, як колекція
  • Інкапсулює великі запити до БД в методи
  • Рівень абстракції між Business Logic рівнем та Data Access рівнем
  • Ізолює програму від змін джерела даних
  • Джерело даних може бути змінено без будь-яких змін в бізнес логіці і з мінімальними змінами в Репозиторії
  • Полегшує автоматизоване юніт тестування, Test Driven Development
  • Легко створювати mock репозиторію

Недоліки

  • Зростає кількість класів
  • Погіршує продуктивність
  • Обмежує у використанні особливостей ОРМ фреймворку

Опис мовою C#

Використаємо Entity Framework. Нехай дано клас-сутність User

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Тепер напишемо інтерфейс репозиторію. Варто зазначити, що репозиторій є колекцією, і має поводитись як колекція, та не містити методів Update(), Save() тощо. Також однією із великих помилок, є те що методи повертають IQueryable замість IEnumerable. Якщо повертати IQueryable це дозволить надбудувати над запитом, ще запити, що не є вірним, оскільки мета цього патерну якраз і є уникнення великих запитів. В такому разі, краще написати ще один метод, який буде виконувати більший запит.

public interface IRepository<TEntity> where TEntity : class
{
    int Count();
    int Count(Expression<Func<TEntity, bool>> predicate);

    IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
                                Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
                                string includeProperties = "",
                                int? page = null, int? amount = null);
    TEntity Get(int id);

    void Insert(TEntity entity);

    void Delete(object id);
    void Delete(TEntity entityToDelete);
    void Delete(Expression<Func<TEntity, bool>> predicate);
}

Тепер реалізуємо цей інтерфейс у вигляді узагальненого класу. При реалізації ми повертаємо сутність, а не DTO. Мапування — це не відповідальність репозиторію.

public class GenericRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
    // FIELDS
    // узагальнений контекст
    protected DbContext dbContext;
    protected DbSet<TEntity> dbSet;

    // CONSTRUCTORS
    public GenericRepository(DbContext dbContext)
    {
        this.dbContext = dbContext;
        this.dbSet = dbContext.Set<TEntity>();
    }

    // METHODS
    public virtual int Count()
    {
        return dbSet.Count();
    }
    public virtual int Count(Expression<Func<TEntity, bool>> predicate)
    {
        return dbSet.Count(predicate);
    }

    public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
                                            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
                                            string includeProperties = "",
                                            int? page = null, int? amount = null)
    {
        // filter
        IQueryable<TEntity> query = dbSet;
        if (filter != null)
        {
            query = query.Where(filter);
        }

        // include properties
        foreach (string includeProperty in includeProperties.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        // ordering
        if (orderBy != null) query = orderBy(query);

        // paging
        if (page.HasValue && amount.HasValue) query = query.Skip((page.Value - 1) * amount.Value).Take(amount.Value);

        return query;
    }
    public virtual TEntity Get(int id)
    {
        return dbSet.Find(id);
    }

    public virtual void Insert(TEntity entity)
    {
        dbSet.Add(entity);
    }

    public virtual void Delete(object id)
    {
        // find
        if (id == null) throw new ArgumentNullException(nameof(id));
        TEntity entityToDelete = dbSet.Find(id);

        // delete finded
        if (entityToDelete == null) throw new InvalidOperationException("There is no records with such id");
        Delete(entityToDelete);
    }
    public virtual void Delete(TEntity entityToDelete)
    {
        if (entityToDelete == null) throw new ArgumentNullException(nameof(entityToDelete));

        if (dbContext.Entry(entityToDelete).State == EntityState.Detached)
        {
            dbSet.Attach(entityToDelete);
        }
        dbSet.Remove(entityToDelete);
    }
    public virtual void Delete(Expression<Func<TEntity, bool>> predicate)
    {
        if (predicate != null) dbSet.RemoveRange(dbSet.Where(predicate));
        else dbSet.RemoveRange(dbSet);
    }
}

Тепер залишилось для кожної сутності реалізувати свій репозиторій. Напишемо інтерфейс, який додаватиме (а можливо і ні) новий функціонал для конкретного репозиторію.

public interface IUserRepository: IRepository<User>
{
    User GetByName(string name);
}

Та конкретна реалізація:

public class UserRepository : GenericRepository<User>, IUserRepository
{
    public UserRepository(DbContext dbContext) 
        : base(dbContext) { }

    public User GetByName(string name)
    {
        return dbSet.First(u => u.Name == name);
    }
}

При потребі варто також узагальнювати тип ключа:

public interface IRepository<TEntity, TKey> where TEntity : class, IEntity<TKey>
{
   . . .
}

Зв'язок з іншими патернами

  • Unit Of Work та Repository часто використовують в парі.

Реалізація

Цей розділ статті ще не написано. Ви можете допомогти проєкту, написавши його. (15 червня 2019)

Див. також

Джерела

  • Implementing the repository and unit of work patterns [Архівовано 14 вересня 2020 у Wayback Machine.]
  • Repository and unit of work pattern [Архівовано 10 серпня 2020 у Wayback Machine.]
  • Common mistakes with the repository pattern [Архівовано 13 червня 2019 у Wayback Machine.]
  • п
  • о
  • р
Основні шаблони
Абстрагування (програмування) • Делегування (Delegation) • Інтерфейс (Interface) • Інтерфейс-маркер (Marker Interface) • Незмінний інтерфейс (Immutable Interface) • Незмінний об'єкт (Immutable Object) • Функціональний дизайн (Functional Design) • Контейнер властивостей (Property Container) • Канал подій (Event Channel)
Твірні шаблони
Абстрактна фабрика (Abstract Factory) • Будівник (Builder) • Одинак (Singleton) • Прототип (Prototype) • Фабричний метод (Factory Method) • Пул об'єктів • Fluent builder • Мультитон • Лінива ініціалізація Отримання ресурсу, як ініціалізація (Resource Acquisition Is Initialization)
Структурні шаблони
Адаптер (Adapter) • Декоратор (Decorator) • Замісник (Proxy) • Компонувальник (Composite) • Міст (Bridge) • Легковаговик (Flyweight) • Фасад (Facade) • Модуль • Виділення приватного класу даних • Близнюки
Шаблони поведінки
Відвідувач (Visitor) • Інтерпретатор (Interpreter) • Ітератор (Iterator) • Команда (Command) • Ланцюжок відповідальностей (Chain of Responsibility) • Посередник (Mediator) • Спостерігач (Observer) • Стан (State) • Стратегія (Strategy) • Знімок (Memento) • Шаблонний метод (Template Method) • Одноразовий відвідувач • Null object • Специфікація • Feature toggleМультиметод • Перехоплювач (Interceptor) • Накопичувач (Collecting Parameter) • Слуга (Servant)
Функційні
Функтор • Генератор • Замикання • Монади • Каррінг • Функція зворотного виклику • Функція вищого порядкуВкладена функція • Результат (Result)
Патерни
конкурентного
програмування
Блокування • Модель акторів • Бар'єр • Монітор • Семафор • М'ютексПланувальник операційної системиЛокальна пам'ять ниток • Оптимістичне блокування (Optimistic Offline Lock) • Песимістичне блокування (Pessimistic Offline Lock) • Активний об'єкт (Active Object)
Кешування
Архітектурні
Базові шаблони
Клієнт-серверна архітектураFront end та back endТриярусна архітектура • Гексагональна архітектура (Архітектура портів та адаптерів) • Відокремлений інтерфейс (Separated Interface) • Сервісно-орієнтована архітектураМікросервісиPush/Pull модель
Шаблони об'єктного структурування
Шаблони представлення
MVCPureMVCHMVCMVPMVVMPost/Redirect/Get
Шаблони предметно-орієнтованого проєктування
Rich/Anemic модельDDD • Інваріант • EntityValue ObjectAggregate RootDTO • Repository • Патерн сервісного рівня (Service Layer) • Фабричний метод (Factory Method) • Специфікація
Шаблони сервісно-орієнтованої архітектури
Архітектура
корпоративних
програмних
додатків
Базові шаблони
Об'єкт-значення (Value Object) • Гроші (Money) • Особливий випадок (Special Case) • Супертип рівня (Layer Supertype) • Відокремлений інтерфейс (Separated Interface) • Шлюз (Gateway) • Розподільник (Mapper) • Реєстр (Registry) • Плагін (Plugin) • Набір записів (Record Set) • Заглушка сервісу (Service Stub)
Шаблони логіки домену
Сценарій транзакції (Transaction script) • Модель предметної області (Domain model) • Обробник таблиці (Table Module) • Патерн сервісного рівня (Service Layer)
Шаблони сховища даних
Активний запис (Active Record) • Шлюз до даних таблиці (Table Data Gateway) • Шлюз до даних запису (Row Data Gateway) • Відображення даних (Data Mapper)
Шаблони об'єктно-реляційної поведінки
Одиниця роботи (Unit Of Work) • Мапа відповідності (Identity Map) • Ліниве завантажування (Lazy Load)
Шаблони об'єктно-реляційного структурування
Поле первинного ключа (Identity Field) • Розмітка зовнішніх ключів (Foreign Key Mapping) • Розмітка зв'язків таблиць (Association Table Mapping) • Відображення залежних об'єктів (Dependent Mapping) • Об'єднане значення (Embedded Value) • Серіалізований великий об'єкт (Serialized LOB) • Наслідування з однією таблицею (Single Table Inheritance) • Наслідування з таблицею для кожного класу (Class Table Inheritance) • Наслідування з таблицею для кожного конкретного класу (Concrete Table Inheritance) • Відображення із наслідуванням (Inheritance Mappers) • База даних звітності
Шаблони обробки об'єктно-реляційних метаданих
Відображення на основі метаданих (Metadata Mapping) • Об'єкт-запит (Query Object) • Сховище (Repository)
Шаблони вебпредставлення
Модель-вид-контролер (Model View Controller) • Контролер сторінки (Page Controller) • Єдина точка входу (Front controller) • Контролер аплікації (Application Controller) • Шаблонізатор (Template View) • Перетворювач (Transform View) • Двокрокова шаблонізація (Two Step View)
Шаблони розподіленої обробки даних
Шаблони локального конкурентного програмування
Оптимістичне блокування (Optimistic Offline Lock) • Песимістичне блокування (Pessimistic Offline Lock) • Блокування із низьким рівнем деталізації (Coarse Grained Lock) • Неявне блокування (Implicit Lock)
Шаблони збереження стану сеансу
Збереження стану сеансу на стороні клієнта (Client Session State) • Збереження стану сеансу на стороні сервера (Server Session State) • Збереження стану сеансу в базі даних (Database Session State)
Тестування
PageObjectМакет об'єкта (Mock Object) • Заглушка сервісу (Service Stub) • Скромний об'єкт (Humble Object)
Інші
Впровадження залежностейIoC контейнер • Локатор служб (Service Locator) • М'яке видалення (Soft Delete) • Auditable Entity • Entity Component System (ECS)Extract, Transform, Load (ETL)
Див. також
Design Patterns (книга) • Бізнес-логіка • Інваріант • Зв'язність (Coupling) • Пов'язаність (Cohesion) • Закон ДеметриKISSDRYYAGNITell Don't Ask • SOLID • CQRSGRASPІдемпотентністьМартін ФаулерАнтипатерн