首页 > 解决方案 > MongoDB 的存储库模式 - 一个事务的多个工作单元

问题描述

我正在使用 Repository + 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 上实现工作单元的具体问题。

标签: c#mongodbtransactionsrepository-patternunit-of-work

解决方案


我从未使用过 MongoDB;对此一无所知。我只是在回答TransactionScope; 所以不确定这是否会对您有所帮助。

请参考TransactionScope 的魔法。IMO,您应该寻找三个因素:

  1. 与数据库的连接应TransactionScope.

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

    不确定,但看起来您可以使用connection.EnlistTransaction(Transaction.Current).

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

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

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

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

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

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

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

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

正如您在评论中提到的,您正在使用async/await.

最后,如果您在 TransactionScope 块中使用 async/await,您应该知道它不适用于 TransactionScope,您可能需要查看 .NET Framework 4.5.1 中接受 TransactionScopeAsyncFlowOption 的新 TransactionScope 构造函数。TransactionScopeAsyncFlowOption.Enabled 选项不是默认选项,它允许 TransactionScope 与异步延续很好地配合。

对于 MongoDB,看看是否对您有帮助。


推荐阅读