C# での linq2sql 挿入トランザクションのロールバックに関する問題
-
19-09-2019 - |
質問
linq2SQLを使用してCSVファイルの内容をデータベーステーブルに挿入しようとしています。
次の場合にトランザクションをロールバックできるようにしたい どれでも の挿入は失敗しますが、このコードを試してみると、次のエラーが発生します - 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();
}
解決
そうですね、順序が間違っています - あなたが電話をかけています 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();
}
}
この場合、コミットは foreach の後に呼び出されますが、 ない 例外が発生してロールバックを実行した場合に呼び出されます。
マルク
他のヒント
ロールバックしてからコミットするからでしょうか?
ロールバックまたはコミットが呼び出されるように、コミットは try ブロック内の最後に配置する必要があります。決して両方はありません...
アップデート:Peter が回答で述べているように、using ブロックは接続を破棄し (したがって Close も)、コミットされていないトランザクションは自動的にロールバックされるため、Close ステートメントも Rollback ステートメントも必要ないと考えています。
「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 の間で例外が発生した場合、トランザクションにはコミットのマークが付けられないため、トランザクションと接続が行 18 で終了するとすぐにロールバックされます。
注記:ここでの動作には違いがありますが、意図的かどうかはわかりません。トランザクションをロールバックするだけでなく、catch ブロックは例外を飲み込むため、エラーが発生したという事実が隠蔽されます。
アップデート: また、SubmitChanges 呼び出しを内部ループの外に移動します。最初に挿入を実行してから、すべての変更に対して 1 回の変更を送信できるはずです。
「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();
}
}
追伸:詳しい説明については、を参照してください。 http://msdn.microsoft.com/en-us/library/bb292288.aspx, これは、「開いた接続を提供した場合、DataContext はそれを閉じません。」