2009年11月8日日曜日

分散トランザクションへの昇格の防止について

ADO.NET2.0では、分散トランザクションへの昇格が自動で行われますが、Windows 2003やXPで開発していて、APサーバとDBサーバが分離されている場合、意図しなかったところで分散トランザクションに昇格して面倒になることがある。

例えば、以下のようなコードを書いたとき、MSDTCを設定していないとエラーとなってしまう。
using (TransactionScope ts = new TransactionScope())
{
DataSet1.ProjectDataTable dt = new DataSet1.ProjectDataTable();
using (ProjectTableAdapter ta = new ProjectTableAdapter())
{
dt = ta.GetData();
dt = ta.GetData();
}
}

エラーとなるのは以下の理由。
1.トランザクションを設定する。
2. 1個目のta.GetData()で、Connection.Open()が呼び出される。
3.GetData()用のSQLが実行される。
4.Connection.Close()が呼び出される。
5.2個目のta.GetData()で、Connection.Open()が呼び出される。
6.トランザクション中にConnection.Open()が2回目に呼び出されたので、トランザクションが分散トランザクション制御になる。
7.DBサーバ側からAPサーバ側に分散トランザクション用のやり取りが始まる。
8.MSDTCの設定で、受信を拒否していたりMSDTCが起動していなかったりすると、分散トランザクション制御の処理が出来ない。
9.トランザクション処理がエラーとなる。

なので、APサーバ側でもMSDTCの受信を許可するようにするか、分散トランザクションへの昇格を防止すればよいことになる。

そもそも、何で、同じテーブルアダプタなのにいちいちConnection.Close()をするかというと、GetData()の中の処理で、Fill()を呼び出しているから。Fill()の処理では、ConnectionがOpen()していないとOpen(),SQL実行,Close()をして、ConnectionがOpenしていると、SQL実行のみするという仕組みのため。

Fillの動きは以下のページを参照。
http://msdn.microsoft.com/ja-jp/library/ms172152(VS.80).aspx

なので、上のプログラムでも、分散トランザクションにしないために、最初からConnection.Open()をしておけばよかったことになる。
つまり、上のプログラムを書き直すと、こんな感じ。
using (TransactionScope ts = new TransactionScope())
{
DataSet1.ProjectDataTable dt = new DataSet1.ProjectDataTable();
using (ProjectTableAdapter ta = new ProjectTableAdapter())
{
ta.Connection.Open();
try
{
dt = ta.GetData();
dt = ta.GetData();
}
finally
{
ta.Connection.Close();
}
}
}

なお、違うクラスのテーブルアダプタでも、接続先DBや接続方法が同じであれば、以下の書き方のように、接続を使いまわすことにより、分散トランザクションへの昇格を回避できます。
using (TransactionScope ts = new TransactionScope())
{
DataSet1.ProjectDataTable dt1 = new DataSet1.ProjectDataTable();
DataSet2.TaskDataTable dt2 = new DataSet2.TaskDataTable();
using (ProjectTableAdapter ta1 = new ProjectTableAdapter())
{
using (TaskTableAdapter ta2 = new TaskTableAdapter())
{

ta1.Connection.Open();
try
{
dt1 = ta1.GetData();
ta2.Connection = ta1.Connection;
dt2 = ta2.GetData();
}
finally
{
ta1.Connection.Close();
}
}
}
}
ただ、テーブルアダプタのConnectionプロパティは、internal指定なので、テーブルアダプタと同じアセンブリからでしか、参照、設定が出来ないので、DLLで分離している場合は注意が必要。

0 件のコメント: