Луковая архитектура, единица работы и общий шаблон репозитория
Это первый раз, когда я внедряю более ориентированный на домен подход. Я решил попробоватьЛуковая Архитектура поскольку он фокусируется на домене, а не на инфраструктуре / платформах / и т.д.
Чтобы абстрагироваться от 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 FrameworkIRepository<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
, классы в основном пустые. Они просто для того, чтобы обеспечить абстракцию. Насколько я понимаю, это согласуется с концепцией Onion Architecture, где инфраструктура и зависимый от платформы код находятся за пределами вашей системы, но наличие пустых классов и пустых интерфейсов кажется неправильным?
Чтобы использовать другую реализацию персистентности (скажем, хранилище таблиц Azure), затем новуюCustomerRepository
класс должен быть создан и будет наследоватьAzureTableStorageRepository<T>
, Но это может привести к избыточному коду (несколько CustomerRepositories)? Как бы этот эффект издевался?
Другая реализация (скажем, хранилище таблиц Azure) имеет ограничения по транснациональной поддержке, поэтому класс AzureTableStorageUnitOfWork не будет работать в этом контексте.
Есть ли другие проблемы с тем, как я это сделал?
(Я взял большую часть моего вдохновения отэта почта)