Problema com Revertendo um linq2sql inserção transação em C #
-
19-09-2019 - |
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();
}
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."