Откат транзакции Entity Framework 6

С EF6 у вас есть новая транзакция, которая может быть использована как:

using (var context = new PostEntityContainer())
        {
            using (var dbcxtransaction = context.Database.BeginTransaction())
            {
                try
                {
                    PostInformation NewPost = new PostInformation()
                    {
                        PostId = 101,
                        Content = "This is my first Post related to Entity Model",
                        Title = "Transaction in EF 6 beta"
                    };
                    context.Post_Details.Add(NewPost);
                    context.SaveChanges();
                    PostAdditionalInformation PostInformation = new PostAdditionalInformation()
                    {
                        PostId = (101),
                        PostName = "Working With Transaction in Entity Model 6 Beta Version"
                    };

                    context.PostAddtional_Details.Add(PostInformation);
                    context.SaveChanges();

                    dbcxtransaction.Commit();
                }
                catch
                {
                    dbcxtransaction.Rollback();
                }
            }
        }

Действительно ли откат необходим, когда все идет вбок? Мне любопытно, потому что в описании Commit сказано: "Выполняет транзакцию основного хранилища".

В то время как описание отката говорит: "Откат основной транзакции хранилища".

Мне это любопытно, потому что мне кажется, что если Commit не вызывается, ранее выполненные команды не будут сохранены (что кажется мне логичным). Но если это так, то зачем было бы называть функцию отката? В EF5 я использовал TransactionScope, у которого не было функции Rollback (только Complete), которая казалась мне логичной. Из-за причин MS DTC я больше не могу использовать TransactionScope, но я также не могу использовать попытку catch, как в примере выше (т.е. Мне нужен только Commit).

Ответ 1

Вам не нужно вызывать Rollback вручную, потому что вы используете оператор using.

DbContextTransaction.Dispose будет вызываться в конце блока using. И он автоматически откатит транзакцию, если транзакция не будет успешно выполнена (не вызванные или обнаруженные исключения). Ниже приведен исходный код метода SqlInternalTransaction.Dispose (DbContextTransaction.Dispose, наконец, делегирует его при использовании поставщика SqlServer):

private void Dispose(bool disposing)
{
    // ...
    if (disposing && this._innerConnection != null)
    {
        this._disposing = true;
        this.Rollback();
    }
}

Вы видите, что он проверяет, не является ли _innerConnection недействительным, если нет, откат транзакции (если она была зафиксирована, _innerConnection будет равна нулю). Посмотрим, что делает Commit:

internal void Commit() 
{
    // Ignore many details here...

    this._innerConnection.ExecuteTransaction(...);

    if (!this.IsZombied && !this._innerConnection.IsYukonOrNewer)
    {
        // Zombie() method will set _innerConnection to null
        this.Zombie();
    }
    else
    {
        this.ZombieParent();
    }

    // Ignore many details here...
}

internal void Zombie()
{
    this.ZombieParent();

    SqlInternalConnection innerConnection = this._innerConnection;

    // Set the _innerConnection to null
    this._innerConnection = null;

    if (innerConnection != null)
    {
        innerConnection.DisconnectTransaction(this);
    }
}

Ответ 2

Пока вы всегда будете использовать SQL Server с EF, нет необходимости явно использовать catch для вызова метода отката. Разрешить автоматическое откат используемого блока для любых исключений всегда будет работать.

Однако, когда вы думаете об этом с точки зрения Entity Framework, вы можете понять, почему все примеры используют явный вызов для отмены транзакции. Для EF поставщик базы данных произволен и подключается, и поставщик может быть заменен на MySQL или любую другую базу данных, имеющую реализацию поставщика EF. Поэтому, с точки зрения EF, нет гарантии, что поставщик автоматически откатится от удаленной транзакции, потому что EF не знает о реализации поставщика базы данных.

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

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

Ответ 3

  • Поскольку вы создали блок "using" для создания экземпляра транзакции, вам не нужно упоминать функцию отката явно, поскольку он будет автоматически откатываться (если только это не было совершенные) на момент удаления.
  • Но если вы создаете экземпляр без использования блока, в этом случае необходимо отменить транзакцию в случае исключения (точно в блок catch), и это тоже с нулевой проверкой для более надежный код. Работа BeginTransaction отличается от транзакции scope (требуется только полная функция, если все операции были успешно завершены). Вместо этого он сродни работе Sql.

Ответ 4

@Mouhong Lin, откуда взялся этот код "this._innerConnection;" Можете ли вы сказать мне, пожалуйста. Мне нужно откатиться, когда у меня был запрос на обновление custum. без использования try-catch.