Откат транзакции 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 не вызывается, ранее выполненные команды не будут сохранены (что мне кажется логичным). Но если это так, то в чем причина вызова функции отката? В EF5 я использовал TransactionScope, который не имел функции отката (только полный), которая казалась мне логичной. Из-за причин MS DTC я больше не могу использовать TransactionScope, но я также не могу использовать try catch, как в приведенном выше примере (т. е. мне нужна только фиксация).

3 65

3 ответа:

вам не нужно звонить 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 будет null). Давайте посмотрим, что 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);
    }
}

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

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

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

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

  1. Так как вы написали блок 'using' для создания экземпляра транзакция, вам не нужно упоминать функцию отката явно, так как он будет автоматически откатываться (если это не было совершено) на момент утилизации.
  2. но если вы создаете его без использования блока, в этом случае это существенно откатить транзакцию в случае исключения (именно в блок catch) и это тоже с нулевой проверкой для более надежный код. Рабочая группа BeginTransaction отличается от транзакции область (которая просто нуждается в полной функции, если все операции были успешно завершены). Вместо этого, это сродни работе Транзакции Sql.