Шаблон репозитория с MongoDB - несколько единиц работы с одной транзакцией
Я реализую слой абстракции DAL поверх драйвера C# Mongo DB, используя шаблон Repository + Unit of Work. Мой текущий дизайн состоит в том, что каждая единица работы открывает (и закрывает) новый сеанс 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.
Я думал о пуле сеансов, так что несколько единиц работы будут использовать один и тот же сеанс и фиксировать / откатывать, когда временная транзакция завершается / прерывается.
Какие еще решения возможны?
Разъяснение:
Вопрос здесь конкретно касается реализации Unit-Of-Work над MongoDB с использованием встроенной поддержки транзакций MongoDB 4.0 (или более поздней).
1 ответ
Я никогда не использовал MongoDB; ничего не знаю об этом. Я отвечаю только с точки зрения TransactionScope
; поэтому не уверен, поможет ли это вам.
Пожалуйста, обратитесь магия TransactionScope. ИМО, есть три фактора, которые вы должны искать:
Подключение к базе данных должно быть открыто внутри
TransactionScope
,Помните, что соединение должно быть открыто внутри блока TransactionScope, чтобы оно автоматически подключалось к внешней транзакции. Если соединение было открыто до этого, оно не будет участвовать в транзакции.
Не уверен, но похоже, что вы можете вручную подключиться к открытому соединению, используя
connection.EnlistTransaction(Transaction.Current)
,Глядя на ваш комментарий и редактирование, это не проблема.
Все операции должны выполняться в одном потоке.
Окружающая транзакция, предоставленная
TransactionScope
является потоковой статической (TLS) переменной. Это может быть доступно с помощью статическогоTransaction.Current
имущество. ЗдесьTransactionScope
код по адресу referenceource.microsoft.com. ThreadStatic ContextData, содержитCurrentTransaction
,а также
Помните, что Transaction.Current является статической переменной потока. Если ваш код выполняется в многопоточной среде, вам может потребоваться принять некоторые меры предосторожности. Соединения, которые должны участвовать в окружающих транзакциях, должны быть открыты в том же потоке, который создает TransactionScope, управляющий этой окружающей транзакцией.
Итак, все операции должны выполняться в одном потоке.
Играть с
TransactionScopeOption
(передать его конструкторуTransactionScope
) значения в соответствии с вашими потребностями.После создания
TransactionScope
посредствомnew
В этом операторе менеджер транзакций определяет, в какой транзакции участвовать. После определения область действия всегда участвует в этой транзакции. Решение основывается на двух факторах: присутствует ли окружающая транзакция и стоимостьTransactionScopeOption
параметр в конструкторе.Я не уверен, что ваш код должен делать. Вы можете играть с этими значениями перечисления.
Как вы упомянули в комментарии, вы используете async/await
,
Наконец, если вы используете async / await внутри блока TransactionScope, вы должны знать, что он плохо работает с TransactionScope, и вы можете захотеть заглянуть в новый конструктор TransactionScope в.NET Framework 4.5.1, который принимает TransactionScopeAsyncFlowOption. Параметр TransactionScopeAsyncFlowOption.Enabled, который не является значением по умолчанию, позволяет TransactionScope хорошо работать с асинхронными продолжениями.
Для MongoDB, посмотрите, поможет ли это вам.
Драйвер MongoDB не знает об окружающих областях TransactionScopes. Вам нужно будет зарегистрироваться с ними вручную или использовать JohnKnoop.MongoRepository, который сделает это за вас: https://github.com/johnknoop/MongoRepository