Вложенные области транзакций, созданные с помощью инициализации объекта, приводят к ошибке

В моем коде C# я использую вложенные области транзакций. У меня есть служебный класс, который одинаково создает объекты TransactionScope. И внешняя область видимости, и внутренняя область действия построены точно таким же образом.

Если я создам объекты TransactionScope, как в первом примере ниже, вложенные области транзакций хорошо сочетаются друг с другом:

public static TransactionScope CreateTransactionScope()
{
   var transactionOptions = new TransactionOptions();
   transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
   transactionOptions.Timeout = TransactionManager.MaximumTimeout;
   return new TransactionScope(TransactionScopeOption.Required, transactionOptions);
}

Однако я получаю исключение, если создаю объекты TransactionScope следующим образом:

public static TransactionScope CreateTransactionScope()
{
   var transactionOptions = new TransactionOptions
   {
      IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted,
      Timeout = TransactionManager.MaximumTimeout
   };
   return new TransactionScope(TransactionScopeOption.Required, transactionOptions);
}

Ошибка гласит: "Транзакция, указанная для TransactionScope, имеет уровень IsolationLevel, отличный от значения, запрошенного для области. Имя параметра: transactionOptions.IsolationLevel".

Может ли кто-нибудь объяснить мне, почему использование инициализации объекта приводит к такому поведению?


person Doodly Studmuffin    schedule 07.03.2013    source источник
comment
Вы нашли проблему?   -  person Saeed Neamati    schedule 27.04.2016


Ответы (2)


Эта ошибка возникает, когда уровень изоляции внешней транзакции отличается от того, который вы хотите назначить области транзакции. Это означает, что при вызове вашего метода и попытке назначить желаемую изоляцию диспетчер транзакций обнаруживает, что существует существующая транзакция с другим уровнем изоляции, и выдает исключение.

var transactionOptions = new TransactionOptions
{
   IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted,
   Timeout = TransactionManager.MaximumTimeout
};

return new TransactionScope(TransactionScopeOption.RequiredNew,TransactionOptions);

RequireNew заставляет создать новую внутреннюю транзакцию. Я предлагаю сделать эту динамическую проверку, которая является текущей изоляцией, примерно так:

if (Transaction.Current != null && Transaction.Current.IsolationLevel != myIsolationLevel)
{
   scopeOption = TransactionScopeOption.RequiresNew;
}

РЕДАКТИРОВАТЬ: Как было предложено в комментариях, стоит отметить, что RequiresNew действительно создает новую транзакцию, и по своей природе она изолирована от внешней транзакции. Я нашел очень хорошее чтение, которое очень хорошо объясняет, как работают транзакции здесь: https://www.codeproject.com/articles/690136/all-about-transactionscope

person Norcino    schedule 19.08.2016
comment
Использование TransactionScopeOption.RequiresNew заставит вложенные транзакции действовать независимо друг от друга. Например, если внутренняя транзакция откатывается, изменения во внешней транзакции не отменяются. Обычно вы этого не хотите, поэтому IMO важно, чтобы это было упомянуто в вашем ответе. - person ajbeaven; 07.05.2018

Вы абсолютно уверены, что только замена вышеуказанных методов приводит к исключению? Они должны быть функционально на 100% эквивалентны.

Просто сухой запуск обоих вариантов в порядке:

using (var aa1 = CreateTransactionScopeGood())
    using (var aa2 = CreateTransactionScopeGood())
        Console.WriteLine("this will be printed");

using (var aa1 = CreateTransactionScopeBad())
    using (var aa2 = CreateTransactionScopeBad())
        Console.WriteLine("this will be printed");

Не могли бы вы предоставить способ воспроизвести его?

Однако я могу воспроизвести ваше исключение только при смешивании разных IsolationScope в одной и той же транзакции, и это действительно должно привести к исключению:

using (new TransactionScope(TransactionScopeOption.Required, new
        TransactionOptions { IsolationLevel = IsolationLevel.Chaos }))
    using (new TransactionScope(TransactionScopeOption.Required, new
            TransactionOptions { IsolationLevel = IsolationLevel.Serializable }))
        Console.WriteLine("this will not be printed");
person Evgeniy Berezovsky    schedule 11.10.2013