MongoDB的存储库模式-一项事务可处理多个工作单元

时间:2019-04-28 06:56:10

标签: c# mongodb transactions repository-pattern unit-of-work

我正在使用“存储库+工作单元”模式在C#Mongo DB驱动程序之上实现DAL抽象层。 我当前的设计是每个工作实例实例都将打开(和关闭)新的Mongo DB会话。 问题在于Mongo DB仅允许会话和事务之间的1:1比率,因此在同一.NET事务下无法进行多个工作单元。

当前实现为:

public class MongoUnitOfWork
{
    private IClientSessionHandle _sessionHandle;

    public MongoUnitOfWork(MongoClient mongoClient)
    {
       _sessionHandle = mongoClient.StartSession();
    }

    public void Dispose()
    {
       if (_sessionHandle != null)
       {
          // Must commit transaction, since the session is closing
          if (Transaction.Current != null)
            _sessionHandle.CommitTransaction();
          _sessionHandle.Dispose();
       }
    }
}

因此,以下代码将无法工作。第一批数据将提前提交:

using (var transactionScope = new TransactionScope())
{
    using (var unitOfWork = CreateUnitOfWork())
    {
       //... insert items

       unitOfWork.SaveChanges();
    }  // Mongo DB unit of work implementation will commit the changes when disposed

    // Do other things

    using (var unitOfWork = CreateUnitOfWork())
    {
       //... insert some more items
       unitOfWork.SaveChanges();
    }
    transactionScope.Complete();
}

显然,直接的答案是将所有更改整合到一个工作单元中,但这并不总是可能的,而且这泄漏了Mongo DB的局限性。

我考虑过会话池,以便多个工作单元将使用同一会话,并在临时事务完成/中止时提交/回滚。

还有哪些其他解决方案呢?

说明:

这里的问题是关于使用MongoDB 4.0(或更高版本)内置事务支持在MongoDB上实现工作单元的。

2 个答案:

答案 0 :(得分:6)

我从未使用过MongoDB;一无所知。我仅以TransactionScope的方式回答;所以不确定是否会帮到您。

请参阅Magic Of TransactionScope。 IMO,您应该寻找三个因素:

  1. 应在TransactionScope 中打开与数据库的连接。

      

    因此请记住,必须在TransactionScope块内打开连接才能使其自动加入环境事务中。如果在此之前打开了连接,则它将不参与事务。

    不确定,但是看起来您可以manually enlist使用connection.EnlistTransaction(Transaction.Current)在作用域外打开连接。

    查看您的评论和编辑,这不是问题。

  2. 所有操作应在同一线程上运行。

      

    TransactionScope提供的环境事务是线程静态(TLS)变量。可以使用静态Transaction.Current属性对其进行访问。这是referencesource.microsoft.com上的TransactionScope代码。 ThreadStatic ContextData,包含CurrentTransaction

      

    请记住,Transaction.Current是线程静态变量。如果您的代码在多线程环境中执行,则可能需要采取一些预防措施。必须参与环境事务的连接必须在创建用于管理该环境事务的TransactionScope的同一线程上打开。

    因此,所有操作都应在同一线程上运行。

  3. 根据需要使用TransactionScopeOption值(将其传递给TransactionScope的构造函数)。

      

    通过TransactionScope语句实例化new时,事务管理器确定要参与的事务。确定后,作用域始终参与该事务。该决定基于两个因素:是否存在环境事务以及构造函数中TransactionScopeOption参数的值。

    我不确定您的代码应该做什么。您可以使用此枚举值。

如评论中所述,您正在使用async/await

  

最后,如果您在TransactionScope块内使用async / await,则应该知道它不能与TransactionScope很好地配合,并且您可能想研究.NET Framework 4.5.1中接受TransactionScopeAsyncFlowOption的新TransactionScope构造函数。 TransactionScopeAsyncFlowOption.Enabled选项(不是默认选项)使TransactionScope在异步继续中发挥良好的作用。

对于MongoDB,请查看this是否对您有所帮助。

答案 1 :(得分:1)

MongoDB驱动程序不了解周围的TransactionScopes。您需要手动征募他们,或者使用JohnKnoop.MongoRepository为您完成此任务:https://github.com/johnknoop/MongoRepository#transactions