C# UnitOfWork(NHibernate),一次只有一个活动UoW/会话?(需要建议)

C# UnitOfWork(NHibernate),一次只有一个活动UoW/会话?(需要建议),c#,nhibernate,architecture,dependency-injection,unit-of-work,C#,Nhibernate,Architecture,Dependency Injection,Unit Of Work,我使用的是NHibernate、DI/IoC和工作单元模式 我所看到的大多数UoW示例都确保在同一时间只能有一个活动UoW/会话,例如和 不幸的是,我还不太明白如何处理两个都使用UoW,但一个调用另一个的服务。 举个例子: 使用UoW的记录器: public class LoggerService : ILoggerService { private ILoggerRepository repo; public LoggerService(ILoggerRepository R

我使用的是NHibernate、DI/IoC和工作单元模式

我所看到的大多数UoW示例都确保在同一时间只能有一个活动UoW/会话,例如和

不幸的是,我还不太明白如何处理两个都使用UoW,但一个调用另一个的服务。
举个例子:

使用UoW的记录器:

public class LoggerService : ILoggerService
{
    private ILoggerRepository repo;

    public LoggerService(ILoggerRepository Repo)
    {
        this.repo = Repo;
    }

    public void WriteLog(string message)
    {
        using (UnitOfWork.Start())
        {
            // write log
        }
    }
}
…以及另一个使用UoW并调用记录器的服务:

public class PlaceOrderService : IPlaceOrderService
{
    private IOrderRepository repo;
    private ILoggerService service;

    public PlaceOrderService(IOrderRepository Repo, ILoggerService Service)
    {
        this.repo = Repo;
        this.service = Service;
    }

    public int PlaceOrder(int orderNumber)
    {
        using (UnitOfWork.Start())
        {           
            // do stuff

            this.service.WriteLog("Order placed!");  // will throw exception!!

            // do more stuff
        }
    }
}
如果我的UoW实现确保同一时间只有一个活动UoW(并且在尝试启动另一个UoW时引发异常,就像在两个链接示例中一样),我的代码将在PlaceOrder方法中的
this.service.WriteLog
行崩溃:
PlaceOrder方法已经创建了一个活动UoW,WriteLog方法将尝试打开第二个,因此UoW实现将因此引发异常

那么,我能做些什么呢?
我想出了两个主意,但在我看来,这两个主意都有点“老套”

  • 不要在LoggerService中启动新的UoW,而是假设调用代码中已经有一个活动的UoW。
    这就是我现在正在做的。我刚刚从LoggerService中删除了使用(UnitOfWork.Start())的
    ,并确保您不能直接调用LoggerService,只能从其他服务中调用。
    这意味着上面的代码可以工作,但是如果调用代码没有启动UoW,LoggerService将崩溃,因为LoggerService假定已经存在UoW

  • 将示例代码保持原样,但将UoW.Start()的实现更改如下:
    a) 如果没有活动UoW,则启动一个新UoW
    b) 如果已有一个活动的UoW,则返回该UoW
    这将使我能够直接或从其他服务调用LoggerService,无论是否已经存在UoW。
    但我从未在网上看到过这样的例子

  • (在这个例子中,只有两个服务。它可能会变得更复杂,只要想想一个
    placespeciallorderservice
    类,它做一些特殊的事情,然后调用
    PlaceOrderService.PlaceOrder()
    。)

    有什么建议吗?
    提前谢谢


    编辑:

    谢谢你迄今为止的回答

    好吧,也许日志记录不是最好的例子。
    我明白您关于使用单独会话进行日志记录的观点,我将对此进行查看并尝试

    无论如何,我仍然需要找到一种方法使嵌套服务调用工作。
    想象一下其他一些代替日志的例子,比如我上面提到的PlaceSpecialLorderService例子

    对于建议我在基础设施的某个地方而不是直接在服务中启动UoW的回答者:
    一方面,这也有道理,但另一方面,这显然意味着我不能在一个服务调用中执行两个不同的事务。
    我必须考虑一下,因为我很确定我会在某个地方需要它(比如:在一个事务中保存订单,然后在第二个事务中做更多的事情,即使失败,订单也不会回滚)

    您是否在应用程序中采用这种方式(每次服务呼叫一个UoW)?

    难道你不需要在同一个服务呼叫中启动第二个UoW吗?

    我认为你发现了一个非常特殊的场景,即你永远不应该在业务服务和日志服务中使用同一个会话。UnitOfWork是“业务事务”,但日志显然不是事务的一部分。如果您的业务逻辑抛出异常,它将回滚您的日志!!!使用单独的会话进行日志记录(使用单独的连接字符串限制在当前事务中登记)。

    我建议启动和停止服务外部的UnitOfWork。我不知道适用于.net世界的合适工具,但您应该寻找一些面向Aspekt的编程工具。或者手动为每个服务创建一个包装类,它只启动工作单元,然后委托给realy服务,然后关闭工作单元。如果一个服务调用另一个服务,它将使用真正的实现,而不是工作单元包装器。

    UOW的构造和生命周期管理不应该是实现业务逻辑的服务所关心的。相反,您应该设置基础设施来进行UOW管理。根据应用程序的不同,可以为每个http请求或每个WCF操作调用设置UOW,也可以为MessageModule(想想NserviceBus)

    大多数DI容器已经支持将实例的生存期与上述上下文关联起来


    关于日志记录,在大多数情况下,它是一个基础设施问题,在订单处理服务旁边有一个日志记录服务是一种气味。让log4net或nlog或任何你喜欢的东西做它们构建的目的

    就我个人而言,我不认为写订单的状态是一个日志问题。对我来说,这是一个商业问题,因此我将重构您的订单服务,使之类似于:

    public int PlaceOrder(int orderNumber)
        {
            using (UnitOfWork.Start())
            {           
                repository.SaveOrder(order)
    
                repository.SaveOrderStatus(order,"Order placed")
    
            }
        }
    

    以及我将用于未处理的执行、身份验证问题等的日志服务。

    我想我自己找到了解决方案

    实际上,这是我在问题中提出的解决方案之一:

    将示例代码保持原样,但将UoW.Start()的实现更改如下:
    a) 如果没有活动UoW,则启动一个新UoW
    b) 如果已存在活动UoW,则返回此UoW
    这将使我能够直接或从其他服务调用LoggerService,无论是否已经存在UoW。
    但我从未在网上看到过这样的例子

    在这里提问之前,我已经想到了这个想法,但我不确定这是否是一个好的解决方案,因为我发现在
    if (HasActiveSession)
    {
        isRootUnitOfWork = false;
        session = GetActiveSession();
    }
    else
    {
        isRootUnitOfWork = true;
        session = CreateSession();
    }