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