Po raz pierwszy wdrażam podejście oparte na domenie bardziej oparte na domenie. Postanowiłem wypróbować model Onion Architecture, ponieważ koncentruje się on na domenie, a nie na infrastrukturze/platformach/etc.Architektura cebuli, jednostka pracy i ogólny wzorzec Repozytorium
W celu streszczenie dala od Entity Framework, mam stworzył rodzajowe repozytorium z jednostka pracy realizacji.
IRepository<T>
i IUnitOfWork
interfejsy:
public interface IRepository<T>
{
void Add(T item);
void Remove(T item);
IQueryable<T> Query();
}
public interface IUnitOfWork : IDisposable
{
void SaveChanges();
}
Entity Framework of IRepository<T>
i 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();
}
}
klienta repozytorium:
public interface ICustomerRepository : IRepository<Customer>
{
}
public class CustomerRepository : EntityFrameworkRepository<Customer>, ICustomerRepository
{
public CustomerRepository(IUnitOfWork unitOfWork): base(unitOfWork)
{
}
}
ASP.NET kontroler MVC pomocą repozytorium
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");
}
}
Zależność wtrysk z sobą
container.RegisterType<IUnitOfWork, EntityFrameworkUnitOfWork>();
container.RegisterType<ICustomerRepository, CustomerRepository>();
rozwiązanie:
problemy?
realizacja Repository (kod EF) jest bardzo ogólne. Wszystko znajduje się po stronie klasy
EntityFrameworkRepository<T>
. Repozytoria konkretnych modeli nie zawierają żadnej z tych logik. To oszczędza mi pisania ton zbędnego kodu, ale ewentualnie poświęcenia elastyczność?Klasy
ICustomerRepository
iCustomerRepository
są w zasadzie puste. Są wyłącznie po to, aby zapewnić abstrakcję. O ile mi wiadomo, pasuje to do wizji architektury cebuli, w której infrastruktura i zależny od platformy kod znajdują się na zewnątrz systemu, ale mając puste klasy i puste interfejsy, czujesz się źle?Aby użyć innej implementacji trwałości (np. Magazyn tabel Azure), należy utworzyć nową klasę
CustomerRepository
i odziedziczyćAzureTableStorageRepository<T>
. Ale może to prowadzić do redundancji kodu (wiele CustomerRepositories)? Jak ten efekt kpi?Inna implementacja (np. Magazyn tabel Azure) ma ograniczenia w zakresie obsługi międzynarodowej, więc klasa AzureTableStorageUnitOfWork nie działałaby w tym kontekście.
Czy są jakieś inne problemy z tym, jak to zrobiłem?
(Brałem większość moich inspiracji this post)
Używasz [wzorca lokalizatora usług] (http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/), biorąc zależność od kontenera IoC. Zamiast tego należy zarejestrować klasę fabryczną, aby wprowadzić ją do kontrolera. Niektóre kontenery IoC mogą wytworzyć jeden z nich dla Ciebie w postaci funkcji 'Func' zależność –
AlexFoxGill