Pergunta

Eu estou tentando inserir o conteúdo de um arquivo CSV em uma tabela de banco de dados usando Linq2Sql.

Eu quero ser capaz de reverter a transação se ANY das inserções falhar, mas quando eu tento com este código eu recebo o seguinte erro na - db.Transaction.Commit ()

System.InvalidOperationException foi não tratada: Este SqlTransaction foi concluída; ele não é mais utilizável é.

Alguém sabe o que eu estou fazendo errado?

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();
}
Foi útil?

Solução

Bem, a ordem é errado - você está chamando db.Transaction.Commit() depois de todo o bloco grande, por isso vai ser chamado, mesmo quando uma exceção ocorreu e você já chamou db.Transaction.Rollback();

Mude seu código para:

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

Neste caso, o commit é chamado após o foreach, mas vai não ser chamado, se você tiver uma exceção e fazer um rollback.

Marc

Outras dicas

Será que é porque você faz o commit depois de fazer a reversão?

Você deve colocar a cometer última dentro do bloco try, então ou reversão ou cometer são chamados. Nunca tanto ...

UPDATE: Como Peter menciona em sua resposta, espero que nem o Fechar ou reverter declarações são necessárias, como o bloco usando disporá (portanto, também Close) a conexão, e uma transação que não é comitted automaticamente deve ser rolado de volta.

Com base no fato de que "usando datacontext" irá garantir que a transação e conexão atual será fechada, vou assumir que o seguinte bloco deve ser suficiente:

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.    }

Se ocorrer uma exceção entre a linha 05 e 16 da transação nunca será marcado com Commit e, portanto, revertida assim que a transação e connetion é finalizado na linha 18.

Nota: há uma diferença no comportamento aqui, que eu não tenho certeza é intencional ou não:., Além de reverter a transação, o bloco catch engole a exceção e assim esconde o fato de que um erro ter ocorrido

Update: Eu também iria mover os SubmitChanges chamar de fora do circuito interno. Você deve ser capaz de primeiro fazer suas inserções e depois a alterações enviar de uma vez por todas as alterações.

No que diz respeito ao código postou em "Peter Lillevold" 's resposta: Eu posso ver que se ocorrer um erro na linha 15, por exemplo, db.SubmitChanges (), ele não fechar a conexão de banco de dados. Portanto, a solução correta é:

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: Para mais explicação, consulte http: // MSDN. microsoft.com/en-us/library/bb292288.aspx , que diz: "Se você fornecer uma conexão aberta, o DataContext não fechará-lo."

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top