3

我正在使用NHibernate,DI/IoC和工作单元模式。UnitOfWork(NHibernate),一次只有一个活动的UoW /会话? (需要咨询)

我见过的大多数UoW示例都要确保同时只能有一个活动的UoW /会话,例如this onethis one

不幸的是,我不太明白,我应该如何处理两个使用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(并且在尝试启动另一个时引发另一个异常,就像在这两个链接的示例中一样),我的代码将在PlaceOrder方法的this.service.WriteLog行中崩溃:
已经有一个由PlaceOrder方法创建的活动UoW,并且WriteLog方法将尝试打开第二个方法,所以UoW实现会因此引发异常。

那么,我能做些什么呢?
我想出了两个想法,但都看起来“哈克”对我来说。

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

  2. 离开示例代码,因为它是,但改变UoW.Start()这样的实现:
    一)如果没有积极的UOW,开始一个新的
    二)如果已经一个活动的UoW,返回这个
    这将使我可以直接调用LoggerService和其他服务,无论是否已经有一个UoW。
    但是我从来没有在网上看过任何这样的例子。

(在这个例子中,只有两个服务,它可以让要复杂得多,只是觉得PlaceSpecialOrderService类,做一些特别的东西,然后调用PlaceOrderService.PlaceOrder()...)

任何建议?
在此先感谢!


编辑:

谢谢你到目前为止的答案。

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

无论如何,我仍然需要找到一种方法来使嵌套服务调用工作。
想象一下其他的例子,而不是记录,就像我上面提到的PlaceSpecialOrderService例子。

向应答者暗示我什么地方开始我的UOW在基础设施,而不是直接在服务:
一方面,这是有道理的太多,但在另一方面,这显然意味着,我不能在一个服务呼叫中做两个不同的交易。
我必须考虑这一点,因为我很确定我需要这个地方(比如:在一个事务中保存订单,然后在第二个事务中做更多的事情,即使失败了,订单不会回滚)。

你是否在你的应用程序中这样做(每个服务调用一个UoW)?
难道你不需要在同一个服务调用中启动第二个UoW的可能吗?

回答

1

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

其实,这是我的问题我提出的解决方案之一:

离开示例代码,因为它是,但改变UoW.Start(执行)这样的:
a)如果有如果已经有一个活动UoW,返回这个
这将使我能够直接调用LoggerService和从其他服务中调用LoggerService,不管是否已经有UoW或不是。
但是我从来没有在网上看过任何这样的例子。

我已经想出了这个想法之前,我在这里问我的问题,但我不知道这是一个很好的解决方案,因为我发现不同的UOW实现负载上了网,但没有类似我的想法。

但我实际上发现了这个实现 - 我读了完整的文章,但我只是不知何故过度阅读相关部分:-)
这是在the first link that I posted in my original question
那里的UoW实现有一个布尔字段“isRootUnitOfWork”。
构造基本上没有这个(简体):

if (HasActiveSession) 
{ 
    isRootUnitOfWork = false; 
    session = GetActiveSession(); 
} 
else 
{ 
    isRootUnitOfWork = true; 
    session = CreateSession(); 
} 

我认为这是最灵活的解决方案。我可以打电话给我的一个服务,或者打电话给另一个......并且这一切都可以工作,而不需要与UoW做任何特殊的技巧。

3

我想你已经找到了一个非常特殊的场景,当你永远不应该使用相同的会话业务服务和日志记录服务。 UnitOfWork是“商业交易”,但记录显然不是交易的一部分。如果您的业务逻辑抛出异常,它会回滚您的日志!使用单独的会话进行日志记录(使用单独的连接字符串限制参与当前事务)。

+0

好的,这是有道理的。但是,我仍然有同样的问题(日志记录只是一个不好的例子),请参阅上文! – 2011-03-23 22:31:49

0

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

2

UOW的构建和生活时间管理不应该是实现您的业务逻辑的服务的关注点。相反,您应该设置您的基础设施来执行UOW管理。根据您的应用程序,您可以为每个http请求或每个WCF操作调用或MessageModule设置一个UOW(请考虑NserviceBus)。

大多数DI容器已经有一些支持将实例的生命周期与上述上下文相关联。

关于登录 - 在最常见的情况下,这是一个基础设施问题,在订单处理服务旁边有一个日志服务是一种气味。让log4net或nlog或者你喜欢做的任何事情都可以做它们的内容。

+0

我不确定每个请求只有一个UoW是否适用。我编辑了我的问题,并添加了我的想法。但是对于没有像业务逻辑那样记录日志的东西你是对的。 – 2011-03-23 22:33:58

+0

由于请求的结果(成功或失败的成功)与UOW的结果相同,通常UOW与一个请求很好地一致。在一个请求中有多个UOW可以使处理错误变得非常棘手。我认为你需要多个UOW的情况可以通过消息传递来解决。 – 2011-03-24 11:47:03

2

就我个人而言,我认为编写订单的状态不是关心伐木。对我来说,这是一个企业的关注,所以我会重构你的订单服务,这样的事情:

public int PlaceOrder(int orderNumber) 
    { 
     using (UnitOfWork.Start()) 
     {   
      repository.SaveOrder(order) 

      repository.SaveOrderStatus(order,"Order placed") 

     } 
    } 

而且日志服务,我会用未处理execptions,认证问题等。

+0

我同意你的意见。在这种特殊情况下,您不希望在事务回滚时记录“下单”消息。然而,在大多数情况下,回滚日志消息让您无法确定事务回滚时发生了什么问题。 – Steven 2011-03-21 08:50:58

+0

你的观点对我而言是有意义的。不过,我认为我选择日志记录并不是一个好主意,因为这似乎分散了我的实际问题。我编辑了我的问题,以便更清楚一点。 – 2011-03-23 22:39:24