С#. Entity. Generic Repository

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

Как правило, репозиторий привязан к одной конкретной сущности или модели, данными которой он управляет. Хотя это необязательно – в репозитории мы можем предусмотреть механизм для загрузки связанных данных из других таблиц, которые связаны с основной моделью, и ряд аналогичных операций. Но тем не менее, часто для управлению одной сущность создается свой репозиторий. Например, если у нас есть классы Phone и Company:

То для каждого из этих классов мы можем создать свой репозиторий.

Если репозитории используют одно и то же подключение, то нередко для организации доступа к одному подключению для всех репозиториев приложения используется другой паттерн – Unit Of Work. Класс, который реализует данный паттерн, как правило, содержит набор репозиториев и ряд некоторых общих для них функций.

Но если мы обратимся непосредственно к Entity Framework, то мы можем увидеть, то он уже реализует паттерны Unit Of Work и репозиторий. К примеру, контекст данных EF для выше обозначенных моделей мог бы выглядеть следующим образом:

Фактически класс ApplicationContext представляет реализацию UnitOfWork – он содержит ряд репозиториев. Каждый репозиторий представлен объектом DbSet, с помощью функциональности которого мы можем получать, добавлять, удалять данные.

Возникает вопрос, а нужно ли нам вообще реализовывать паттерн репозиторий, если мы работаем с EF? Ответ зависит от конкретной ситуации. Если мы будем использовать только EF и больше ничего, то для управления доступа к данным нам не надо создавать никаких дополнительных репозиториев. К тому же для многих распространенных СУБД уже есть свои провайдеры для EF 6, поэтому при наличии одного и того же кода мы относительно легко сможем перейти от использования одной СУБД к другой. Основные изменения будут касаться прежде всего конфигурации проекта.

Однако если мы полагаемся на ряд СУБД, которые могут не иметь нормальных провайдеров для EF 6 и интерфейс работы которых сильно отличается от той функциональности, которую предоставляет нам EF, то чтобы привести все технологии работы с БД к общему знаменателю, мы можем реализовать паттерн репозиторий.

К примеру реализуем паттерн репозиторий для работы через EF 6. Вначале создадим интерфейс репозитория:

Если у нас несколько классов, функциональность работы с которыми совпадает, то мы можем реализовать Generic Repository, который может работать с разными сущностями.

Теперь создадим базовую реализацию для репозитория, которая применяет Entity Framework:

Репозиторий хранит ссылку на контекст и набор DbSet для работы с текущей сущностью. Все методы репозитория фактически вызывают методы DbSet и контекста данных.

Стоит отметить, что в конце каждого метода на изменение данных вызывается метод _context.SaveChanges(). При реализации паттерна UnitOfWork этот метод, как правило, вызывается отдельно и реализуется в самом классе UnitOfWork.

Отдельно стоит сказать про загрузку связанных данных. Если у нас навигационные свойства помечены как виртуальные, то с помощью Lazy Loading связанные данные автоматически будут подгружаться к загруженным сущностям. Однако в примере с моделями выше навигационные свойства не виртуальные, и для загрузки данных следует использовать Eager Loading, то есть нам надо использовать метод Include(). Однако чтобы использовать этот метод, нам надо точно знать, какие навигационные свойства надо использовать для подгрузки связанных классах, что в случае с generic-реализацией маловероятно. Тем не менее мы можем это сделать.

Итак, добавим в класс репозитория следующие методы:

Для загрузки связанных данных здесь определен вспомогательный метод Include(). Используя переданный в качестве параметра массив выражений Include и метод Aggregate, он составляет запрос в виде переменной query, которая возвращается в качестве результата.

Этот метод реализуется в методе GetWithInclude(), который возвращает массив объектов. Перегруженная версия метода GetWithInclude также добавляет дополнительное условие.

Применение подобных методов:

 

This entry was posted in C#. Bookmark the permalink.