|
分類:[.NET 全般]
SqlCommandを使用し、PK が IDENTITY のフィールドのテーブルに insert文を実行し、 そのときに採番される PK値 を取得するときに、 複数のスレッドで実行するとエラーが発生しました。
ですが、なぜエラーになるのか分からないので、 お分かりになる方がいらっしゃったら教えてください。
環境は 言語:Microsoft Visual C# 2013 DB:Sqlserver 2008 OS:Windows 2008R2 です。
現象とすると、冒頭に書いた通り、 ========================================================= PK が IDENTITY のフィールドのテーブルに insert文を実行し、 そのときに採番される PK値 を取得する ========================================================= と言うことを、メインスレッド+サブスレッドで並行に実行すると問題ないのですが、 メインスレッドは何もせず、サブスレッド×2で並行に実行すると、 System.InvalidOperationExceptionが発生し、 追加情報として、「この Command に関連付けられている DataReader が既に開かれています」 のエラーは発生します。
サンプルコードは下記の通りです。 ======================================================== using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms;
namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); }
private void Form1_Load(object sender, EventArgs e) { Debug.WriteLine("step1");
//メインスレッドと別スレッドで並行して登録を行う(DB/ID/PW 等は伏せてます) using (var conn1 = new SqlConnection("data source=xxx.xxx.xxx.xxx;initial catalog=xxxxx;integrated security=false;persist security info=True;User ID=xxxxx;Password=xxxxx;Pooling=false")) using (var conn2 = new SqlConnection("data source=xxx.xxx.xxx.xxx;initial catalog=xxxxx;integrated security=false;persist security info=True;User ID=xxxxx;Password=xxxxx;Pooling=false")) { conn1.Open(); conn2.Open();
using (SqlCommand cmd = new SqlCommand()) { cmd.Connection = conn1; //必要に応じて実行 cmd.CommandText = "drop table test1"; cmd.ExecuteNonQuery();
cmd.CommandText = "create table test1(f1 int IDENTITY(1,1)PRIMARY KEY, f2 varchar(10), f3 int)"; cmd.ExecuteNonQuery(); }
ActionClass action1 = new ActionClass(conn1, "action1");
Task task1 = new Task(action1.Action);
task1.Start();
ActionClass action2 = new ActionClass(conn2, "action2"); action2.Action();
task1.Wait(); }
Debug.WriteLine("step2");
//メインスレッドはただ待つだけ。2つのスレッドが並行して登録を行う using (var conn1 = new SqlConnection("data source=xxx.xxx.xxx.xxx;initial catalog=xxxxx;integrated security=false;persist security info=True;User ID=xxxxx;Password=xxxxx;Pooling=false")) using (var conn2 = new SqlConnection("data source=xxx.xxx.xxx.xxx;initial catalog=xxxxx;integrated security=false;persist security info=True;User ID=xxxxx;Password=xxxxx;Pooling=false")) { conn1.Open(); conn2.Open();
ActionClass a1 = new ActionClass(conn1, "action1"); ActionClass a2 = new ActionClass(conn2, "action2");
Task task1 = new Task(a1.Action); Task task2 = new Task(a1.Action);
task1.Start(); task2.Start();
task1.Wait(); task2.Wait(); } }
class ActionClass { SqlConnection conn; string id;
internal ActionClass(SqlConnection conn, string id) { this.conn = conn; this.id = id; }
internal void Action() { using (SqlCommand cmd = new SqlCommand()) { cmd.Connection = conn; for (int i = 0; i < 100; i++) { cmd.CommandText = string.Format("insert test1(f2, f3) values ('{0}', {1}); select SCOPE_IDENTITY();", id, i); // step2が出力された後、ここでエラーが発生する Debug.WriteLine(id + ":" + cmd.ExecuteScalar()); } } } } } } ========================================================
上記を実行すると、イミディエイトウィンドウには下記のように表示されます (一部編集しています) ======================================================== step1 action2:1 action1:2 action2:3 action1:4 :action1とaction2のメッセージの繰り返し action2:97 action1:98 action1:100 action2:99 step2 action1:102 action1: action1:101 action1:103 action1:104 action1:105 action1:106 action1:107 action1:108 action1:109 action1:110 action1:111 action1:112 action1:113 action1:114 action1:115 action1:116 action1:117 action1:118 action1:119 action1:120 型 'System.InvalidOperationException' の初回例外が System.Data.dll で発生しました action1:121 ========================================================
step1とstep2で行っていることは同じだと思うのですが、 なぜ、step2では ======================================================== System.InvalidOperationException この Command に関連付けられている DataReader が既に開かれています ======================================================== が発生するのでしょうか? 複数のTaskで実行すると、エラーメッセージ通りの同一のDataReaderが使用されるということでしょうか?
それとも環境に依存するのでしょうか?
お分かりになる方がいらっしゃったら、ご回答よろしくお願いします。
|