使用MongoDB的存储库模式 - 具有一个事务的多个工作单元

问题描述 投票:7回答:1

我正在使用Repository + Unit of Work模式在C#Mongo DB驱动程序之上实现DAL抽象层。我目前的设计是每个工作单元实例打开(并关闭)一个新的Mongo数据库会话。问题是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# mongodb transactions repository-pattern unit-of-work
1个回答
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是否可以帮助您。

© www.soinside.com 2019 - 2024. All rights reserved.