Шаблон репозитория с 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. ИМО, есть три фактора, которые вы должны искать:

  1. Подключение к базе данных должно быть открыто внутри TransactionScope,

    Помните, что соединение должно быть открыто внутри блока TransactionScope, чтобы оно автоматически подключалось к внешней транзакции. Если соединение было открыто до этого, оно не будет участвовать в транзакции.

    Не уверен, но похоже, что вы можете вручную подключиться к открытому соединению, используя connection.EnlistTransaction(Transaction.Current),

    Глядя на ваш комментарий и редактирование, это не проблема.

  2. Все операции должны выполняться в одном потоке.

    Окружающая транзакция, предоставленная TransactionScope является потоковой статической (TLS) переменной. Это может быть доступно с помощью статического Transaction.Current имущество. Здесь TransactionScope код по адресу referenceource.microsoft.com. ThreadStatic ContextData, содержит CurrentTransaction,

    а также

    Помните, что Transaction.Current является статической переменной потока. Если ваш код выполняется в многопоточной среде, вам может потребоваться принять некоторые меры предосторожности. Соединения, которые должны участвовать в окружающих транзакциях, должны быть открыты в том же потоке, который создает TransactionScope, управляющий этой окружающей транзакцией.

    Итак, все операции должны выполняться в одном потоке.

  3. Играть с 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

Другие вопросы по тегам