- PVSM.RU - https://www.pvsm.ru -
Привет. Приступим.
Итак, что предлагает эта статья. Вы подключаете 2 nuget-пакета, реализуете для своих Entity простой интерфейс IRetrievableEntity<TEntity, TId> (можно упростить задачу, отнаследовавшись от готового класса Entity<TId>), добавляете в код 2 строки регистрации и получаете на выходе полную независимость от DBContext и возможность резолвить репозитории для каждой IRetrievableEntity-сущности с возможностью построения объектно-ориентированных (типизированных) запросов к этим репозиториям. Только посмотрите:
var employeeRepository = container.Resolve<IRepository<Emloyee, int>>();
var employees = employeeRepository.Get(q =>
{
q = q.Filter(e => e.EmploymentDate >= new DateTime(2014, 9, 1));
if(excludeFired)
q = q.Filter(e => !e.Fired);
q = q.Include(e => e.Department, p => p.Department.Chief)
.OrderBy(p => p.FirstName);
});
Можно использовать репозитории без IoC, получив бонусы построения запросов и изоляции от контекста, но следующий пример и исходники дадут исчерпывающую информацию о наиболее продуктивном и простом применении.
1. Установить пакеты Rikrop.Core.Data [1] и Rikrop.Core.Data.Unity [2]. Первый — в проект с Entity-сущностями, второй — в проект с контекстом БД. Я для примера использовал один проект, получилось следующее:
<packages>
<package id="EntityFramework" version="5.0.0" targetFramework="net45" />
<package id="Rikrop.Core.Data" version="1.0.1.0" targetFramework="net45" />
<package id="Rikrop.Core.Data.Unity" version="1.0.1.0" targetFramework="net45" />
<package id="Unity" version="3.5.1404.0" targetFramework="net45" />
</packages>
2. Добавить к регистрациям в IoC примерно следующее:
container.RegisterRepositoryContext<MyDbContext>();
//container.RegisterRepositoryContext(s => new MyDbContext(s), "myConStr");
container.RegisterRepositories(typeof(Department).Assembly);
RepositoryContext [3] это обёртка над классом DBContext, соответственно, регистрация принимает generic-параметр наследника от DBContext. Можно регистрировать контекст с именем строки подключения.
Метод-расширение RegisterRepositories принимает на вход Assembly, в которой расположены POCO-объекты, реализующие IRetrievableEntity<TId> [4].
3. Реализовать для своих POCO IRetrievableEntity. Например:
public class Department : Entity<Int32>, IRetrievableEntity<Department, Int32> {...}
public class Employee : DeactivatableEntity<Int32>, IRetrievableEntity<Employee, Int32> {...}
4. Готово. Можно пользоваться:
var departmentRepository = container.Resolve<IRepository<Department, int>>();
departmentRepository.Save(new Department { Name = "TestDepartment" });
var testDeps = departmentRepository.Get(q => q.Filter(dep => dep.Name.Contains("Test")));
Ошибиться невозможно, поскольку generic-параметры следят за тем, чтобы резолвились правильные репозитории:
// Разрешить IDeactivatableRepository для департамента нельзя (ошибка компиляции),
// т.к. эта сущность не относледована от DeactivatableEntity.
//var departmentRepository2 = container.Resolve<IDeactivatableRepository<Department, int>>();
5. Если стандартной фунциональности, предлагаемой интерфейсами IRepository<TEntity, in TId> [5] и IDeactivatableRepository<TEntity, in TId> [6] для какой-либо сущности окажется недостаточно, всегда можно расширить существующую реализацию в пару простых шагов. Задаем интерфейс:
public interface IPersonRepository : IDeactivatableRepository<Person, int>
{
void ExtensionMethod();
}
Добавляем реализацию и обязательно помечем атрибутом:
[Repository(typeof(IPersonRepository))]
public class PersonRepository : DeactivatableRepository<Person, int>, IPersonRepository
{
public PersonRepository(IRepositoryContext repositoryContext)
: base(repositoryContext)
{
}
public void ExtensionMethod()
{
// Здесь у вас будет доступ к DBContext
Console.WriteLine("PersonRepository ExtensionMethod called");
}
}
Просим Unity найти и зарегистрировать все расширенные репозитории в заданной сборке:
// Пример регистрации "расширенных" репозиториев без указания их типа.
container.RegisterCustomRepositories(typeof(Department).Assembly);
Пользуемся:
// Извлечение "расширенного" репозитория по интерфейсу.
var personRepository = container.Resolve<IPersonRepository>();
personRepository.ExtensionMethod();
При этом без необходимости в расширенных методах всегда можно воспользоваться стандартной реализацией:
// Для класса Person репозиторий зарегистрирован под обоими интерфейсами, поскольку сущность наследуется от DeactivatableEntity.
var personRepository2 = container.Resolve<IRepository<Person, int>>();
var personRepository3 = container.Resolve<IDeactivatableRepository<Person, int>>();
Есть базовая реализация [7] репозитория, которая работает с контекстом через абстракцию IRepositoryContext. Обращение к набору данных из репозитория работает благодаря generic-методам DBContext:
public override DbSet<TEntity> Data { get { return Context.Set<TEntity>(); } }
Ключевым классом для работы с построением запросов к репозиторию служит класс RepositoryQuery [8]. Класс реализует fluent interface и позволяет делать Include по Expression или по текстовому пути (последнее может быть актуально при загрузке свойств дочерних коллекций, когда путь невозможно указать через expression), фильтровать, сортировать, Skip и Take.
Магия регистрации основана на Reflection. При регистрации репозиториев в сборке находятся все классы, отнаследованные от IRetrievableEntity<,>, из них достаются generic-аргументы, строятся новые типы IRepository<,> и Repository<,> с нужными generic-аргументами, дальше всё это регистрируется по свежесозданным через рефлексию типам. Для расширенных репозиториев поиск происходит по атрибуту:
foreach (var repositoryType in assembly.GetTypes().Where(type => type.IsClass))
{
var repositoryAttribute = repositoryType.GetCustomAttribute<RepositoryAttribute>();
if (repositoryAttribute != null)
{
container.RegisterType(repositoryAttribute.RepositoryInterfaceType,
repositoryType, new TransientLifetimeManager());
}
}
Автор: Vadimyan
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/repository/74646
Ссылки в тексте:
[1] Rikrop.Core.Data: https://www.nuget.org/packages/Rikrop.Core.Data/
[2] Rikrop.Core.Data.Unity: https://www.nuget.org/packages/Rikrop.Core.Data.Unity/
[3] RepositoryContext: https://github.com/rikrop/Rikrop.Core.Data/blob/master/Rikrop.Core.Data/Repositories/RepositoryContext.cs
[4] IRetrievableEntity<TId>: https://github.com/rikrop/Rikrop.Core.Data/blob/master/Rikrop.Core.Data/Entities/Contracts/IRetrievableEntity.cs
[5] IRepository<TEntity, in TId>: https://github.com/rikrop/Rikrop.Core.Data/blob/master/Rikrop.Core.Data/Repositories/Contracts/IRepository.cs
[6] IDeactivatableRepository<TEntity, in TId>: https://github.com/rikrop/Rikrop.Core.Data/blob/master/Rikrop.Core.Data/Repositories/Contracts/IDeactivatableRepository.cs
[7] базовая реализация: https://github.com/rikrop/Rikrop.Core.Data/blob/master/Rikrop.Core.Data/Repositories/RepositoryBase.cs
[8] RepositoryQuery: https://github.com/rikrop/Rikrop.Core.Data/blob/master/Rikrop.Core.Data/Repositories/RepositoryQuery.cs
[9] Rikrop.Core.Data: https://github.com/rikrop/Rikrop.Core.Data/
[10] Rikrop.Core.Data.Unity: https://github.com/rikrop/Rikrop.Core.Data.Unity/
[11] lexwings: http://habrahabr.ru/users/lexwings/
[12] Источник: http://habrahabr.ru/post/243353/
Нажмите здесь для печати.