Entity Framework пытается восстановиться после распределенной транзакции

Работа над проектом WebApi, поддерживаемым mssql с EntityFramework, а также Oracle (12c) с использованием oracle ManagedDataAccess.Client.OracleConnection. Мы используем autofac для внедрения экземпляра нашего контекста на каждый запрос, но весь доступ к оракулу осуществляется только по специальному запросу.

У нас есть определенные операции, которые зависят от обеих баз данных одновременно, поэтому мы решили использовать объект TransactionScope для управления транзакцией.

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

Данный:

public void Test() 
{
    var preItem = new HelpItem
    {
        Field1 = "pre batch";
    };
    _context.Items.Add(preItem);
    _context.SaveChanges(); // This save always works.

    var batchResult = FooService.BatchOperation(true);

    var postItem = new HelpItem
    {
        Field1 = "post batch";
    };
    _context.Items.Add(postItem);
    _context.SaveChanges(); // This will succeed/fail depending on whether FooService caused a distributed transaction.
}

С методом BatchOperation как:

public Result BatchOperation(bool triggerDtc) 
{
    using (var transaction = new new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
    {
        if (triggerDtc){
            // Make requests to both databases.
        } else {
            // Make request to one database.
        }

        // Always complete for the sake of the demonstration.
        transaction.Complete();
    }
}

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

Ошибка:

Распределенная транзакция завершена. Либо зачислите этот сеанс в новую транзакцию, либо в транзакцию NULL.

Каков был бы правильный способ справиться с этим?

В этом конкретном случае вы можете просто создать другую транзакцию вокруг второй части:

var batchResult = FooService.BatchOperation(true);

using (var transaction = new new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
    var postItem = new HelpItem
    {
        Field1 = "post batch";
    };
    _context.Items.Add(postItem);
    _context.SaveChanges(); // This save depends on whether FooService caused a distributed transaction.
    transaction.Complete();
}

Но эта проблема возникла из-за того, что метод FooService.BatchOperation был изменен с помощью простого поиска в другой базе данных, неосознанно нарушая все методы, которые продолжают использовать контекст после его вызова. С обычной транзакцией один контекст EF может свободно использоваться в них и из них без проблем, есть ли способ добиться того же с распределенной транзакцией?

РЕДАКТИРОВАТЬ: Это действительно меня сейчас смутило. Простого выполнения запроса в другой (нераспределенной) области транзакций достаточно, чтобы восстановить функциональность EF.

public IHttpActionResult Test() 
{
    var preItem = new HelpItem
    {
        Field1 = "pre batch";
    };
    _context.Items.Add(preItem);
    _context.SaveChanges(); // This save works.

    var batchResult = FooService.BatchOperation(true);

    using (var transaction = new new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
    {
        var lookupAnything = _context.Items.ToList();
        transaction.Complete(); // This is optional, because we really don't care and it's disposed either way.
    }

    var postItem = new HelpItem
    {
        Field1 = "post batch";
    };
    _context.Items.Add(postItem);
    _context.SaveChanges(); // Now this always works.
}

Очевидно, я не могу просто везде размещать это, поэтому до сих пор не уверен, каково фактическое решение.


person Wrightboy    schedule 02.05.2017    source источник
comment
Соединение контекста существует и используется до создания области транзакции и после ее удаления. Я узнал трудным путем, что это вызывает проблемы.   -  person Gert Arnold    schedule 02.05.2017
comment
Это на самом деле неправильно. И вы определенно можете создать транзакцию постфактум. См., например, эту суть: проблема, с которой вы столкнулись в своей ссылке, заключалась просто в том, что вы не указывали уровень изоляции при создании нового TransasctionScope, поэтому он использовал уровень Serializable по умолчанию (yikes), который отличается от ReadCommitted в mssql/ef, что предотвратило автоматическое зачисление.   -  person Wrightboy    schedule 03.05.2017