NHibernate を使用する Web アプリケーションでトランザクションを処理するための最適なソリューションを見つけようとしています。
IHttpModule を使用し、HttpApplication.BeginRequest で新しいセッションを開き、それを ManagedWebSessionContext.Bind(context, session); で HttpContext にバインドします。HttpApplication.EndRequest でセッションを閉じてバインドを解除します。
リポジトリ基本クラスでは、ベスト プラクティスに従って、SaveOrUpdate、Delete、Get メソッドの周りに常にトランザクションをラップしました。
public virtual void Save(T entity)
{
var session = DependencyManager.Resolve<ISession>();
using (var transaction = session.BeginTransaction())
{
session.SaveOrUpdate(entity);
transaction.Commit();
}
}
しかし、保存、削除などの複数のリポジトリ呼び出しを含めるためにアプリケーションサービスなどのどこかにトランザクションを配置する必要がある場合、これは機能しません。
そこで私たちが試みたのは、TransactionScope を使用することです (独自のトランザクション マネージャーを作成したくありませんでした)。これが機能することをテストするために、.Complete() を呼び出さない外側の TransactionScope を使用してロールバックを強制します。
リポジトリ保存():
public virtual void Save(T entity)
{
using (TransactionScope scope = new TransactionScope())
{
var session = DependencyManager.Resolve<ISession>();
session.SaveOrUpdate(entity);
scope.Complete();
}
}
リポジトリを使用するブロック:
TestEntity testEntity = new TestEntity { Text = "Test1" };
ITestRepository testRepository = DependencyManager.Resolve<ITestRepository>();
testRepository.Save(testEntity);
using (var scope = new TransactionScope())
{
TestEntity entityToChange = testRepository.GetById(testEntity.Id);
entityToChange.Text = "TestChanged";
testRepository.Save(entityToChange);
}
TestEntity entityChanged = testRepository.GetById(testEntity.Id);
Assert.That(entityChanged.Text, Is.EqualTo("Test1"));
これはうまくいきません。しかし、NHibernate が TransactionScope をサポートしていれば、私にはそうなるでしょう! 何が起こるかというと、データベースにはまったく ROLLBACK がありませんが、testRepository.GetById(testEntity.Id); ステートメントが実行されると、SET Text = "TestCahgned" を指定した UPDATE が代わりに実行されます (BEGIN TRAN と ROLLBACK TRAN の間で実行されるはずです)。NHibernate は level1 キャッシュから値を読み取り、UPDATE をデータベースに送信します。予期しない動作!? 私が理解していることから、NHibernate のスコープでロールバックが行われるたびに、現在のセッションを閉じてバインドを解除する必要もあります。
私の質問は: TransactionScope と ManagedWebSessionContext を使用してこれを行う良い方法を知っている人はいますか?