我正在使用 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,您应该寻找三个因素:
应在
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)
在范围外打开的连接。查看您的评论和编辑,这不是问题。
所有操作都应该在同一个线程上运行。
The ambient transaction provided by the
TransactionScope
is a thread-static (TLS) variable. It can be accessed with staticTransaction.Current
property. Here is theTransactionScope
code at referencesource.microsoft.com. ThreadStatic ContextData, contains theCurrentTransaction
.和
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.
所以,所有操作都应该在同一个线程上运行。
玩
TransactionScopeOption
(将其传递给TransactionScope
的构造函数)值根据您的需要。Upon instantiating a
TransactionScope
by thenew
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 theTransactionScopeOption
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/