Куда относится Единица работы с EF4, IoC (Unity) и репозиторием?
Я вижу несколько вопросов, относящихся в некоторой степени к этому, но я все еще не могу найти ответ, который ищу, поэтому я публикую свой вопрос. Если другой вопрос содержит ответ (а я его просто не вижу), укажите мне на него.
Я пытаюсь выяснить, где принадлежит мой UnitOfWork и, в частности, он создается при использовании EF4 и Unity с шаблоном Repository.
По сути, у меня есть сервис, который используется для реализации моей бизнес-логики. Этот конструктор службы принимает хранилище, поэтому служба добавляется в мое хранилище. Затем служба использует внедренный репозиторий для выполнения действий с хранилищем данных, но мне нужно обернуть их в единицу работы.
Моя единица работы, однако, должна быть внедрена с контекстом EF4 (или, в моем случае, и с интерфейсом контекста - IObjectContext). И я не уверен, где UoW должен быть создан и введен в контексте.
Вот возможные варианты, которые я могу придумать, ни один из которых не кажется идеальным:
Включите UoW в конструктор службы, таким образом, добавив службу с единицей работы, которая, в свою очередь, будет введена с моим контекстом EF4. Но это кажется неправильным, потому что я не хочу, чтобы мой UoW создавался в каждом экземпляре хранилища.
Выполните создание по требованию, используя container.Resolve, чтобы получить экземпляр UoW, внедряя мой контекст EF4. Это кажется чрезмерным из-за того, что вам постоянно приходится попадать в контейнер IoC, а не иметь доступ к UoW.
Внедрить контекст непосредственно в сервис, что позволяет мне создавать UoW(контекст). Это кажется плохим, так как я теперь предоставил контекст сервису, и он должен быть изолирован от репозитория.
Итак, мой вопрос, является ли один из этих методов приемлемым, или есть другой метод, о котором я не думаю?
Заранее спасибо.
2 ответа
Есть, вероятно, несколько способов, как использовать это, поэтому я опишу тот, который я нашел полезным.
Imho место для определения UoW находится в логике приложения - логике, которая вызывает ваш бизнес-уровень (бизнес-сервисы). Причина этого в том, что UoW должен представлять логическую бизнес-операцию - логика приложения (или фасад службы в случае удаленных вызовов) определяет, что такое логическая транзакция. Так, например, в MVC вы можете использовать архитектуру, в которой каждое действие контроллера представляет собой одно UoW:
public class MyController : Controller
{
public MyController(IFirstService firstService, ISecondService secondService,
IUnitOfWork unitOfWork)
{ ... }
[HttpPost]
public ActionResult SomeAction(Model data)
{
_firstService.SomeProcessing(data);
_secondService.SomeProcessing(data);
_unitOfWork.SaveChanges();
return RedirectToAction(...);
}
}
В этом примере мой контроллер зависит от двух бизнес-сервисов, и действие вызывает их оба - UoW, затем сохраните изменения, выполненные обеими сервисами. Вот почему я считаю, что UoW должен быть доступен в контроллере, потому что, если у вашего прикладного уровня нет доступа к UoW, вы не можете составить (повторно использовать) свою логику из нескольких вызовов службы (поскольку каждый из них, вероятно, вызывает свои собственные SaveChanges).
Другой подход с сервисным фасадом. Фасад будет публичным интерфейсом вашего бизнес-уровня и будет скрывать состав сервиса:
_firstService.SomeProcessing(data);
_secondService.SomeProcessing(data);
_unitOfWork.SaveChanges();
В этом случае UoW не будет передан контроллеру, а на служебный фасад, а служебный фасад будет введен в контроллер. Вы обязательно будете использовать этот подход, если ваша бизнес-логика будет представлена через веб-сервис (или другую удаленную технологию).
Последняя проблема, с которой вам приходится сталкиваться, это передача UoW сервисам. Службы, а также UoW внедряются в контроллер (презентатор, фасад службы или что-то еще), но в то же время UoW (или ObjectContext) должны вводиться в службы, чтобы внутренне используемые репозитории могли работать с ним. Для этого вам нужен правильный менеджер времени жизни IoC, чтобы он возвращал один и тот же экземпляр для всех инъекций в рамках одного и того же "запроса". В случае веб-приложения вам нужен менеджер времени жизни PerHttpRequest (который вы должны реализовать сами, потому что Unity его не предоставляет).
Одним из способов управления этим является использование метода, описанного в http://mfelicio.wordpress.com/2010/02/07/managing-the-entity-framework-objectcontext-instance-lifetime-in-wcf-and-sharing-it-among-repositories/ Эта статья реализует ContextManager для сервисов Wcf. Для приложения ASP.NET мы могли бы использовать что-то вроде этого.
public class AspNetDBContextManager<TContext> : IDBContextManager
where TContext : IDBContext, new()
{
#region IDBContextManager Members
public IDBContext GetDBContext()
{
return this.GetOrCreateDbContext();
}
private IDBContext GetOrCreateDbContext()
{
if (HttpContext.Current == null)
{
throw new InvalidOperationException("Can be used only within ASP.NET applications");
}
string dbContextKey = string.Format("__AspNetDBCM__{0}__", HttpContext.Current.GetHashCode());
object dbContext = HttpContext.Current.Items[dbContextKey];
if (dbContext == null)
{
dbContext = new TContext();
if (dbContext != null)
{
HttpContext.Current.Items[dbContextKey] = dbContext;
}
}
return dbContext as IDBContext;
}
#endregion
}
public interface IDBContext
{
object Context { get; }
}
public interface IDBContextManager
{
IDBContext GetDBContext();
}