Проблема с откатом транзакции вставки linq2sql в C # - PullRequest
1 голос
/ 05 октября 2009

Я пытаюсь вставить содержимое файла CSV в таблицу базы данных, используя linq2SQL.

Я хочу иметь возможность откатить транзакцию, если ЛЮБОЙ вставок не удалось, но когда я пытаюсь с этим кодом, я получаю следующую ошибку в - db.Transaction.Commit ()

Исключение System.InvalidOperationException не обработано: эта SqlTransaction завершена; его больше нельзя использовать.

Кто-нибудь знает, что я делаю не так?

using (DataContext db = new DataContext())
{
    db.Connection.Open();
    db.Transaction = db.Connection.BeginTransaction();

    try
    {
        foreach (string entry in entries)
        {
            XXX xxx = new XXX()
            {
                P1 = "something",
                P2 = "something"
            };

            db.XXXX.InsertOnSubmit(xxx);
            db.SubmitChanges();
        }
    }
    catch (Exception)
    {
        db.Transaction.Rollback();
    }
    finally
    {
        db.Connection.Close();
    }

    db.Transaction.Commit();
}

Ответы [ 4 ]

3 голосов
/ 05 октября 2009

Ну, порядок неправильный - вы звоните db.Transaction.Commit() после всего большого блока, так что он будет вызываться, даже если произошло исключение, и вы уже позвонили db.Transaction.Rollback();

Измените свой код на:

using (DataContext db = new DataContext())
{
    db.Connection.Open();
    db.Transaction = db.Connection.BeginTransaction();

    try
    {
        foreach (string entry in entries)
        {
            ....
            db.XXXX.InsertOnSubmit(xxx);
            db.SubmitChanges();
        }

        db.Transaction.Commit(); <== CALL HERE !!
    }
    catch (Exception)
    {
        db.Transaction.Rollback();
    }
    finally
    {
        db.Connection.Close();
    }
}

В этом случае ваш Commit вызывается после foreach, но он будет НЕ вызываться, если вы столкнетесь с исключением и выполните откат.

Марк

2 голосов
/ 29 июля 2010

Что касается кода, размещенного в ответе "Peter Lillevold": Я вижу, что если в строке 15 возникает ошибка, например, db.SubmitChanges (), он не закрывает соединение с базой данных. Следовательно, правильное решение:

enter code here
      using (DataContext db = new DataContext())
        {
            // The dispose method of DbConnection will close any open connection
            // and will rollback any uncommitted transactions
            using (DbConnection dbConnection = db.Connection)
            {
                dbConnection.Open();
                db.Transaction = dbConnection.BeginTransaction();
                foreach (string entry in entries)
                {
                    XXX xxx = new XXX()
                    {
                        P1 = "something",
                        P2 = "something"
                    };
                    db.XXXX.InsertOnSubmit(xxx);
                }
                db.SubmitChanges();
                db.Transaction.Commit();
            }
        } 

PS: Для более подробного объяснения, пожалуйста, обратитесь к http://msdn.microsoft.com/en-us/library/bb292288.aspx,, который говорит: «Если вы предоставите открытое соединение, DataContext не закроет его».

2 голосов
/ 05 октября 2009

На основании того факта, что «использование datacontext» обеспечит закрытие текущей транзакции и соединения, я буду считать, что следующего блока должно быть достаточно:

01.    using (DataContext db = new DataContext())
02.    {    
03.        db.Connection.Open();    
04.        db.Transaction = db.Connection.BeginTransaction();    
05.
06.        foreach (string entry in entries)        
07.        {                
08.            XXX xxx = new XXX()                
09.            {                        
10.                P1 = "something",                        
11.                P2 = "something"                
12.            };                
13.            db.XXXX.InsertOnSubmit(xxx);                
14.        }    
15.        db.SubmitChanges();        
16.
17.        db.Transaction.Commit();
18.    }

Если между строками 05 и 16 возникает исключение, транзакция никогда не будет помечена как Commit и будет отменена, как только транзакция и подключение завершатся в строке 18.

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

Обновление: Я бы также переместил вызов SubmitChanges из внутреннего цикла. Сначала вы сможете выполнить вставки, а затем отправить изменения один раз для всех изменений.

2 голосов
/ 05 октября 2009

Это потому, что вы делаете коммит после выполнения отката?

Вы должны поместить коммит последним внутри блока try, чтобы вызывался откат или коммит. Никогда оба ...

ОБНОВЛЕНИЕ: Как Питер упоминает в своем ответе, я ожидаю, что ни операторы Close, ни Rollback не нужны, так как блок using удалит (и, следовательно, также закроет) соединение, а транзакция, которая не завершена, должна автоматически откатиться. назад.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...