Мне нравится комбинировать DI с фильтром действий, таким образом, контейнер может управлять областью работы, но метод begin / commit / rollback вызывается для вас автоматически, и вам не придется возиться с этим на каждом действие.
Это немного сложнее, потому что обычно фильтры действий НЕ повторно создаются для каждого запроса, поэтому, когда у вас есть зависимость, которой вы действительно хотите быть для запроса (единица работы), вам нужно поработать над магией.
Вот как я это делаю, используя Ninject и Ninject.Web.Mvc
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class UnitOfWorkAction : Attribute
{
}
public class UnitOfWorkActionFilter : IActionFilter
{
private IUnitOfWork _unitOfWork;
public UnitOfWorkActionFilter(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
_unitOfWork.Begin();
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Exception == null)
{
try
{
_unitOfWork.Commit();
}
catch
{
_unitOfWork.Rollback();
throw;
}
}
else
{
_unitOfWork.Rollback();
}
}
}
Затем я настраиваю, как атрибут должен использоваться в App_Start / NinjectMVC3.cs
kernel.BindFilter<UnitOfWorkActionFilter>(FilterScope.Action, 0)
.WhenActionMethodHas<UnitOfWorkAction>();
//also make sure your IUnitOfWork is bound per request, obviously
И, наконец, пример Действие
[UnitOfWorkAction]
public ActionResult SomeAction(int id)
{
//invoke your services here, and the uow will be automatically committed or rolled back when the action returns
}
Также стоит отметить, что этот подход позволяет вам вносить конструктор зависимостей в ваш фильтр действий, а не просто вводить свойства, что я очень предпочитаю.