ZombieCheck Exception - это SqlTransaction завершено; он больше не используется - во время простого коммита

У меня есть следующий код, который выполняет фиксацию одной строки в таблице базы данных (SQL 2008/.NET 4)

using (var db = new MyDbDataContext(_dbConnectionString))
{
    Action action = new Action();
    db.Actions.InsertOnSubmit(dbAction);
    db.SubmitChanges();
}

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

System.InvalidOperationException: This SqlTransaction has completed; it is no longer usable.
at System.Data.SqlClient.SqlTransaction.ZombieCheck()
at System.Data.SqlClient.SqlTransaction.Rollback()
at System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)

На SO есть ряд похожих вопросов, но после их чтения я не могу решить причину.

Может ли это быть просто из-за таймаута SQL (исключение происходит близко к 25 секундам после вызова)? Или я должен ожидать исключения тайм-аута SQL в этом случае?

Кто-нибудь знает, что еще может вызвать это?

Ответ 1

DataContext.SubmitChanges имеет следующие строки кода в теле:

// ...
try
{
    if (this.provider.Connection.State == ConnectionState.Open)
    {
        this.provider.ClearConnection();
    }
    if (this.provider.Connection.State == ConnectionState.Closed)
    {
        this.provider.Connection.Open();
        flag = true;
    }
    dbTransaction = this.provider.Connection.BeginTransaction(IsolationLevel.ReadCommitted);
    this.provider.Transaction = dbTransaction;
    new ChangeProcessor(this.services, this).SubmitChanges(failureMode);
    this.AcceptChanges();
    this.provider.ClearConnection();
    dbTransaction.Commit();
}
catch
{
    if (dbTransaction != null)
    {
        dbTransaction.Rollback();
    }
    throw;
}
// ...

Когда время соединения заканчивается, выполняется блок catch, а строка dbTransaction.Rollback(); выдаст InvalidOperationException.

Если бы у вас был контроль над кодом, вы могли бы получить исключение, подобное этому:

catch
{
    // Attempt to roll back the transaction. 
    try
    {
        if (dbTransaction != null)
        {
            dbTransaction.Rollback();
        }
    }
    catch (Exception ex2)
    {
        // This catch block will handle any errors that may have occurred 
        // on the server that would cause the rollback to fail, such as 
        // a closed connection.
        Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
        Console.WriteLine("  Message: {0}", ex2.Message);
    }
    throw;
}

Ответ 3

ДА! Я была такая же проблема. Страшный ответ заключается в том, что SQLServer иногда откатывает транзакцию на стороне сервера при возникновении ошибки и не передает ошибку клиенту. YIKES!

Посмотрите на группу Google microsoft.public.dotnet.framework.adonet для "Ошибка SqlTransaction.ZombieCheck" Colberd Zhou [MSFT] объясняет это очень хорошо.

и см. комментарий aef123 на этом сообщении SO