アプリケーションでデータベース アクセスを行う必要がある場合は、次のパターンを使用します。
- クエリを実行するために、メソッドを呼び出す
CreateOpenConnection
だけのメソッドを持つ静的ファクトリ クラスがあります。このメソッドは、クエリを実行する前に呼び出され、クエリが返された後に接続が破棄されます。new SqlConnection(myConnectionString)
Open()
work.Commit()
挿入/更新/削除の場合、次のような呼び出しで変更がバッチ処理されてデータベースに送信される Unit of Work パターンを使用します。
work.Commit:
using (var tranScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
using (var conn = DapperFactory.CreateOpenConnection())
{
var count = _changeTracker.CommitChanges(conn);
tranScope.Complete();
return count;
}
}
これは、Web サービスの一部としての一般的な使用にはうまく機能するようですが、現在、これを Rebus と組み合わせて使用しようとすると、MSDTC の問題が発生します。
私が知る限り、Rebus (キュー内のメッセージを処理するとき) は新しいメッセージを作成するTransactionScope
ので、メッセージの処理に失敗した場合はロールバックできます。さて、これ自体はこれまでのところうまくいきました。SqlConnection
Rebus メッセージ ハンドラー内で問題なくnew を開くことができます (ただし、同じ Rebus 内で従来の Entity Framework クエリと手動の SqlConnections を使用TransactionScope
しても機能しませんが、現時点では問題とは考えていません)。しかし、昨日、次の質問をしました。
答えは、Rebus のサガ機能を使用することです。これを実装して、Rebus saga が新しい SQL Server データベースに永続化されるように構成してみました (個別の接続文字列を使用)。おそらく、その SQL Server の永続性を使用するSqlConnection
と、独自SqlConnection
の
分散トランザクション マネージャー (MSDTC) のネットワーク アクセスが無効になっています。コンポーネント サービス管理ツールを使用して、MSDTC のセキュリティ構成でネットワーク アクセスに対して DTC を有効にしてください。
MSDTC を有効にすることは、構成とパフォーマンスのオーバーヘッドに関して、私が非常に避けたいことです。そして、私は間違っているかもしれませんが、それは必要ではないようです.
ここで起こっていると私が推測するのは、Rebus がアンビエントTransactionScope
を作成し、SqlConnection
それが作成した がそのスコープに参加するということです。また、独自のものを作成SqlConnection
しようとすると、そのアンビエント スコープにも参加しようとし、複数の接続が関係しているため、MSDTC に昇格されますが、失敗します。
これを修正する方法についてのアイデアはありますが、それが正しいことかどうかはわかりません。私がすることは次のとおりです。
- アプリケーションの接続文字列に追加
Enlist=false
して、アンビエント トランザクションに参加しないようにします。 - メソッドを変更し
Commit
て、新しいものを作成しないようにしますTransactionScope
(接続はこれ以上サブスクライブしないと言ったので、これ以上サブスクライブしません)が、conn.BeginTransaction
.
そのようです:
var transaction = conn.BeginTransaction();
try
{
var count = _changeTracker.CommitChanges(conn);
transaction.Commit();
return count;
}
catch
{
transaction.Rollback();
throw;
}
finally
{
transaction.Dispose();
}
これが正しいアプローチであるかどうか、および考えられる欠点が何であるかはわかりません。
任意のヒント?
更新:明確にするために、work.Commit()
問題を引き起こしているのはそれではありません。うまくいくと確信していますが、クエリが失敗するため、そこにたどり着くことはありません。
失敗するものの例:
public int? GetWarehouseID(int appID)
{
var query = @"
select top 1 ID from OrganizationUnits o
where TypeID & 16 = 16 /* warehouse */";
using (var conn = _dapper.OpenConnection())
{
var id = conn.Query<int?>(query).FirstOrDefault();
return id;
}
}
これは、 Rebus によって aTransactionScope
が作成されたとき、および Rebus によって aSqlConnection
が開かれた後に呼び出されます。my を開くSqlConnection
と、参加しようとしてクラッシュします