在ASP.NET MVC 3中的许多服务之间共享工作单元

4 c# architecture asp.net-mvc

我的控制器使用2个或更多服务.反过来,我的服务构造并使用他们自己的Unit of Work类的实例(可以访问存储库).

我希望我的服务共享一个相同的工作单元实例,并使其可单元测试.我的问题是:

  1. 我应该将工作单元和服务注入我的控制器吗?
  2. 我还需要将工作单元注入我的服务中.我该怎么办?非常感谢.

Bro*_*ook 7

我喜欢将DI与动作过滤器结合起来,这样容器可以管理工作单元的范围,但是会自动为您调用begin/commit/rollback,并且您不必在每个动作上都大惊小怪.

这有点棘手,因为通常情况下,每个请求都不会重新实例化actionfilters,所以当你有一个依赖,你想要按照请求(工作单元),你需要工作一些魔术.

以下是我使用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();
                }
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后我配置如何在App_Start/NinjectMVC3.cs中使用该属性

kernel.BindFilter<UnitOfWorkActionFilter>(FilterScope.Action, 0)
                .WhenActionMethodHas<UnitOfWorkAction>();
//also make sure your IUnitOfWork is bound per request, obviously
Run Code Online (Sandbox Code Playgroud)

最后,一个示例动作

[UnitOfWorkAction]
public ActionResult SomeAction(int id)
{
     //invoke your services here, and the uow will be automatically committed or rolled back when the action returns
 }
Run Code Online (Sandbox Code Playgroud)

另外值得注意的是,这种方法允许您对动作过滤器进行构造函数注入依赖项,而不仅仅是属性注入,这是我非常喜欢的.


Moh*_*bed 5

1)我不认为在UI控制器中注入工作单元是一个好主意,尝试将逻辑和事务与UI分开.

2)是的,您可以在服务中注入UoW,最好是通过IoC容器注入的构造函数.有些人将它设计为静态工厂,但我更喜欢将它用作构造函数中注入的参数

public class MyService : IMyService
{
  IUnitOfWork _unitOfWork;

  public MyService(IUnitOfWork uow)
  {
    _unitOfWork = uow;
  }

  public void DoSomeOperation(SampleParam param)
  {
    _unitOfWork.BeginTrx();
    //  do some work 
    _unitOfWork.Commit();
  }
}
Run Code Online (Sandbox Code Playgroud)

或使用静态工厂

public class MyService : IMyService
{
  public void DoSomeOperation(SampleParam param)
  {
    using(UnitOfWork.Start())
    {
      //  do some work 
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 管理服务中的工作单元与管理存储库中的工作单元有同样的问题,即您现在无法轻松地将多个服务调用绑定到事务中,并且失败会导致数据保留在一个奇怪的国家.如果服务需要调用另一个服务,这会变得更加复杂.我认为最好的解决方案是在99%的时间内将UoW的管理留给应用程序/ ui层.另外1%你需要对一个复杂的过程进行细粒度控制,这很好,但它是例外,而不是常态. (2认同)