c# - MongoDB 的存储库模式 - 一个事务的多个工作单元

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

我正在使用 Repository + Unit of Work 模式在 C# Mongo DB 驱动程序之上实现 DAL 抽象层。 我当前的设计是每个工作单元实例打开(和关闭)一个新的 Mongo DB session 。 问题是 Mongo DB 只允许 session 和事务之间的比例为 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 的限制。

我想到了 session 池,这样多个工作单元将使用同一个 session ,并在 transient 事务完成/中止时提交/回滚。

还有哪些可能的解决方案?

澄清:

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

最佳答案

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

请引用 Magic Of TransactionScope . IMO,您应该寻找三个因素:

  1. 应在TransactionScope 内部打开与数据库的连接.

    So remember, the connection must be opened inside the TransactionScope block for it to enlist in the ambient transaction automatically. If the connection was opened before that, it will not participate in the transaction.

    不确定,但看起来你可以 manually enlist使用 connection.EnlistTransaction(Transaction.Current) 在范围外打开的连接。

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

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

    The ambient transaction provided by the TransactionScope is a thread-static (TLS) variable. It can be accessed with static Transaction.Current property. Here is the TransactionScope code at referencesource.microsoft.com. ThreadStatic ContextData, contains the CurrentTransaction.

    Remember that Transaction.Current is a thread static variable. If your code is executing in a multi-threaded environments, you may need to take some precautions. The connections that need to participate in ambient transactions must be opened on the same thread that creates the TransactionScope that is managing that ambient transaction.

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

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

    Upon instantiating a TransactionScope by the new statement, the transaction manager determines which transaction to participate in. Once determined, the scope always participates in that transaction. The decision is based on two factors: whether an ambient transaction is present and the value of the TransactionScopeOption parameter in the constructor.

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

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

Lastly, if you are using async/await inside the TransactionScope block, you should know that it does not work well with TransactionScope and you might want to look into new TransactionScope constructor in .NET Framework 4.5.1 that accepts a TransactionScopeAsyncFlowOption. TransactionScopeAsyncFlowOption.Enabled option, which is not the default, allows TransactionScope to play well with asynchronous continuations.

对于 MongoDB,查看 this帮助你。

关于c# - MongoDB 的存储库模式 - 一个事务的多个工作单元,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55887792/

相关文章:

c# - 实现通用存储库和工作单元

mongodb - db.mydb.find({tag :'java' }).count() 和 db.mydb.count({tag :'java' }) 在 mongodb 中有什么区别

javascript - 在mongodb中将字符串转换为日期?

firebase云功能事务偶尔工作

mysql - 事务和提交

c# - LINQ to Entities 无法识别方法 Generic.List(int) 到 Generic.IEnumerable(int) 方法

c# - xUnit C# 代码片段可加快测试创建速度

c# - 如何向 ListBoxItem 添加上下文菜单?

node.js - 带有 docker 的 Mongo 副本 Node

python - 跨集团 (XG) 交易和使用的进一步说明