Это первый раз, когда я применяю подход к проектированию, ориентированный на предметную область. Я решил попробовать Onion Architecture, поскольку она ориентирована на домен а не в инфраструктуре / платформах / и т. д.
Чтобы абстрагироваться от Entity Framework, я создал общий репозиторий с реализацией Единицы работы.
Интерфейсы IRepository<T>
и IUnitOfWork
:
public interface IRepository<T>
{
void Add(T item);
void Remove(T item);
IQueryable<T> Query();
}
public interface IUnitOfWork : IDisposable
{
void SaveChanges();
}
Реализации Entity Framework IRepository<T>
и IUnitOfWork
:
public class EntityFrameworkRepository<T> : IRepository<T> where T : class
{
private readonly DbSet<T> dbSet;
public EntityFrameworkRepository(IUnitOfWork unitOfWork)
{
var entityFrameworkUnitOfWork = unitOfWork as EntityFrameworkUnitOfWork;
if (entityFrameworkUnitOfWork == null)
{
throw new ArgumentOutOfRangeException("Must be of type EntityFrameworkUnitOfWork");
}
dbSet = entityFrameworkUnitOfWork.GetDbSet<T>();
}
public void Add(T item)
{
dbSet.Add(item);
}
public void Remove(T item)
{
dbSet.Remove(item);
}
public IQueryable<T> Query()
{
return dbSet;
}
}
public class EntityFrameworkUnitOfWork : IUnitOfWork
{
private readonly DbContext context;
public EntityFrameworkUnitOfWork()
{
this.context = new CustomerContext();;
}
internal DbSet<T> GetDbSet<T>()
where T : class
{
return context.Set<T>();
}
public void SaveChanges()
{
context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
Репозиторий Клиент:
public interface ICustomerRepository : IRepository<Customer>
{
}
public class CustomerRepository : EntityFrameworkRepository<Customer>, ICustomerRepository
{
public CustomerRepository(IUnitOfWork unitOfWork): base(unitOfWork)
{
}
}
Контроллер ASP.NET MVC с использованием репозитория:
public class CustomerController : Controller
{
UnityContainer container = new UnityContainer();
public ActionResult List()
{
var unitOfWork = container.Resolve<IUnitOfWork>();
var customerRepository = container.Resolve<ICustomerRepository>();
return View(customerRepository.Query());
}
[HttpPost]
public ActionResult Create(Customer customer)
{
var unitOfWork = container.Resolve<IUnitOfWork>();
var customerRepository = container.Resolve<ICustomerRepository>();;
customerRepository.Add(customer);
unitOfWork.SaveChanges();
return RedirectToAction("List");
}
}
Внедрение зависимости с единством:
container.RegisterType<IUnitOfWork, EntityFrameworkUnitOfWork>();
container.RegisterType<ICustomerRepository, CustomerRepository>();
Решение:
ПРОБЛЕМЫ?
Реализация репозитория (код EF) очень универсальна. Все это находится в стороне от класса
EntityFrameworkRepository<T>
. Репозитории конкретных моделей не содержат такой логики. Это избавляет меня от написания тонны избыточного кода, но, возможно, приносит в жертву гибкость?Классы
ICustomerRepository
иCustomerRepository
в основном пусты. Они созданы исключительно для того, чтобы обеспечить абстракцию. Насколько я понимаю, это соответствует видению луковой архитектуры, где инфраструктура и платформенно-зависимый код находятся вне вашей системы, но иметь пустые классы и пустые интерфейсы кажется неправильным?Чтобы использовать другую реализацию сохранения (например, хранилище таблиц Azure), необходимо создать новый класс
CustomerRepository
, который унаследуетAzureTableStorageRepository<T>
. Но это может привести к избыточному коду (несколько репозиториев клиентов)? Как бы этот эффект издевательства?Другая реализация (например, хранилище таблиц Azure) имеет ограничения на международную поддержку, поэтому класс AzureTableStorageUnitOfWork не будет работать в этом контексте.
Есть ли другие проблемы с тем, как я это сделал?
(В основном я черпал вдохновение из этого сообщения)
Func<T>
зависимости. - person AlexFoxGill   schedule 10.12.2013