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

Posted

技术标签:

【中文标题】在 ASP.NET MVC 3 中的多个服务之间共享一个工作单元【英文标题】:Sharing a Unit of Work among many Services in ASP.NET MVC 3 【发布时间】:2011-11-30 23:13:30 【问题描述】:

我的控制器使用 2 个或更多服务。反过来,我的服务构建并使用它们自己的工作单元类实例(可以访问存储库)。

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

    我应该将工作单元和服务注入我的控制器吗? 我还需要将工作单元注入到我的服务中。在哪里 我应该这样做吗?非常感谢。

【问题讨论】:

【参考方案1】:

我喜欢将 DI 与操作过滤器结合使用,这样容器可以管理工作单元的范围,但是会自动为您调用开始/提交/回滚,您不必每次都为此大惊小怪行动。

这有点棘手,因为通常情况下,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();
                
        
    

然后我在 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
 

另外值得注意的是,这种方法让您可以将依赖项注入到您的操作过滤器中,而不仅仅是属性注入,我非常喜欢这种方法。

【讨论】:

【参考方案2】:

1) 我不认为在 UI 控制器中注入 Unit of Work 是一个好主意,尝试将逻辑和事务与 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();
  

或者使用静态工厂

public class MyService : IMyService

  public void DoSomeOperation(SampleParam param)
  
    using(UnitOfWork.Start())
    
      //  do some work 
    
  

【讨论】:

我在管理服务中的工作单元时遇到了与在存储库中管理它相同的问题,即您现在无法轻松地将多个服务调用绑定到一个事务中,并且失败会导致数据处于奇怪的状态。如果一个服务需要调用另一个服务,这会变得更加复杂。我认为最好的解决方案是 99% 的时间将 UoW 的管理留给应用程序/ui 层。另外 1% 您需要对复杂过程进行细粒度控制,这可以正常工作,但这是例外,而不是常态。 我同意布鲁克的观点。服务层应该是可重用的,并且与使用它的应用程序无关。当您将 UoW 放入服务层时,您也在为消费应用程序指定 UoW 范围。考虑一个桌面应用程序,它允许您在同一表单上维护多个实体的数据。当您单击“保存”时,应用程序会在提交 UoW 之前调用 4 或 5 个服务方法。例如,如果 UoW 位于服务层,则用于编辑移动应用上相同数据的较小子集的更精简的 UI 将无法重用服务层代码。【参考方案3】:

我喜欢 Mohamed Abed 的回答,我还没有获得足够的分数来为他的帖子添加评论,所以我只想提一下,在这些情况下,您需要确保将工作单元标记为单个实例,否则将不会在服务之间共享。

【讨论】:

以上是关于在 ASP.NET MVC 3 中的多个服务之间共享一个工作单元的主要内容,如果未能解决你的问题,请参考以下文章

在 2 个控制器 ASP.NET Core 3.1 MVC 之间传递 ID

七天学会ASP.NET MVC ——ASP.NET MVC 数据传递

在 ASP.Net-MVC 中的控制器之间传递信息

如何在 ASP.NET MVC 的视图中调用多个操作?

在asp.net mvc 3中的后台作业/计划任务中发送电子邮件

asp.net中的三层架构是啥意思?mvc设计模式是啥?它们之间有关系吗?