Відкат транзакцій Entity Framework 6


82

З 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();
                }
            }
        }

Чи потрібен відкат насправді, коли справа йде боком? Мені цікаво, оскільки в описі коміту сказано: "Фіксує базову транзакцію магазину".

Тоді як в описі відкоту сказано: "Відкочує базову транзакцію магазину".

Це мене цікавить, бо мені здається, що якщо коміт не буде викликаний, то попередньо виконані команди не будуть зберігатися (що мені здається логічним). Але якщо це так, яка причина буде викликати функцію відкоту? У EF5 я використовував TransactionScope, який не мав функції відкату (лише Complete), що мені здавалося логічним. Через причини MS DTC я більше не можу використовувати TransactionScope, але я також не можу використовувати спробний улов, як у прикладі вище (тобто, мені потрібен лише коміт).


1
Чи читали ви про транзакції в sql ? EF намагається імітувати це. AFAIK, якщо ви не здійсните транзакцію в sql, вона буде відкатана.
gunr2171

Також дивіться це питання .
gunr2171

Так, я знаю про транзакції в самому SQL. Мені було цікаво, що робить EF, але якщо вони імітують це, це має сенс. Я подивлюсь, чи зможу я обійти це. Дякую!
Печиво Пес

SaveChanges () завжди трапляється в транзакції, яка буде відкатана, якщо трапиться виняток. У вашому випадку немає необхідності намагатися обробляти це вручну (у цьому конкретному випадку було б навіть краще додати всі об'єкти спочатку і SaveChangesлише один раз).
Павел,

Я хочу, щоб елементи зберігалися з обох SaveChanges лише тоді, коли обидва не виходять з ладу, тому так, мені потрібна одна транзакція навколо них обох.
The Cookies Dog

Відповіді:


117

Вам не потрібно телефонувати 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не має значення null, якщо ні, відкат транзакції (якщо здійснено, _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);
    }
}

23

Поки ви завжди будете використовувати SQL Server з EF, немає необхідності явно використовувати catch для виклику методу відкоту. Завжди дозволятиме використанню блоку автоматичний відкат у будь-яких винятках.

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

Отже, як найкраща практика, документація EF рекомендує явно відмовитись - на випадок, якщо колись ви зміните постачальника на реалізацію, яка не виконує автоматичний відкат при утилізації.

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


1
Дякую за це розуміння. Я заглибився в код, і ви потрапили до абстрактного класу DbTransaction's Dispose, який замінено в SqlTransaction, який сам називає SqlInternalTransaction, згаданий Mouhong Lin.
ShawnFumo

4
  1. Оскільки ви написали блок "за допомогою" для створення екземпляра транзакції, вам не потрібно чітко згадувати функцію відкату, оскільки вона буде автоматично відкатана (якщо це не було здійснено) під час видалення.
  2. Але якщо ви створили екземпляр без використання блоку, у такому випадку важливо повернути транзакцію у випадку винятку (саме в блоці catch), і це також із нульовою перевіркою більш надійного коду. Робота BeginTransaction на відміну від області транзакцій (яка просто потребує повної функції, якщо всі операції були успішно завершені). Натомість це схоже на роботу транзакцій Sql.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.