サービスレイヤーでトランザクションを管理する方法は?
-
28-10-2019 - |
質問
次のアーキテクチャを使用して.NETアプリケーションを開発しています。プレゼンテーションレイヤー(ASP.NET MVC 2を使用したMVCパターンを使用)、サービスレイヤー、データアクセスレイヤー(エンティティフレームワーク上のリポジトリパターンを使用)。
トランザクション管理をサービスレイヤーに入れることにしましたが、それを実装する方法についてはわかりません。サービスレイヤーレベルで完全にトランザクションを制御したいと考えています。つまり、コントローラーがサービスレイヤーのメソッドを呼び出すたびに、データベースの更新に関する原子操作でなければなりません。
サービスレイヤーで提供される異なるサービス間に関係がなかった場合、それは簡単です。各メソッドは、実行の最後に変更をコミットする必要があります(つまり、使用するコンテキストで保存メソッドを呼び出します)。しかし、サービスレイヤーでのサービスが一緒に機能する場合があります。
例:次のパラメーターを受信する確認方法を備えた出荷サービスを提供します:出荷ID、それが新しい顧客または既存の顧客に対応するかどうかを示すフラグ、顧客ID(出荷確認が既存のものである場合に備えて顧客)と顧客名(新しい顧客向けの場合)。フラグが「新しい顧客」に設定されている場合、サービスレイヤーは(a)顧客を作成し、(b)出荷を確認する必要があります。 (a)貨物サービスは、カスタマーサービスを呼び出します(新しい顧客を作成してデータベースに保存するために必要な検証とロジックを既に実装しています)。
このシナリオで変更を犯すのは誰ですか?
- カスタマーサービスはそれをすべきですか?新しい顧客を作成した後に変更をコミットすることはできません。なぜなら、出荷確認方法の後半で何か問題が発生する可能性があるため、直接呼び出される場合(他のユースケースでは、クライアントを作成するために提供される場合)。
- サービスメソッドを呼び出すコントローラーはそれを行う必要がありますか?しかし、コントローラーはトランザクションについて何も知らないはずです。すべてのトランザクションKnowLadgeをサービスレイヤーに配置することにしました。
- サービスレイヤーのトランザクションマネージャー?それを設計する方法?、誰がそれをいつ呼びますか?
これに従うべきデザインパターンはありますか?
解決
私のサービスにはコミット()があります。これは、コンストラクターで渡された場合、サービスによってUnitofworkが作成された場合にのみコミットします。
サービスに2番目の(内部)コンストラクターを使用しました。
public class MyService
{
private IUnitOfWork _uow;
private bool _uowInternal;
public MyService()
{
_uow = new UnitOfWork();
_uowInternal = false;
}
internal MyService(IUnitOfWork uow)
{
_uow = uow;
_uowInternal = true;
}
public MyServiceCall()
{
// Create second service, passing in my UnitOfWork:
var svc2 = new MySecondService(_uow);
// Do your stuff with both services.
....
// Commit my UnitOfWork, which will include all changes from svc2:
Commit();
}
public void Commit()
{
if(!_uowInternal)
_uow.Commit();
}
}
他のヒント
EFの代わりにWCFとL2を使用した同様のアーキテクチャでは、メインサービスインターフェイス実装クラスでトランザクションを使用することを選択しました。使用しました Transactionscope これを達成するために:
public void AServiceMethod() {
using(TransactionScope ts = new TransactionScope()) {
service1.DoSomething();
service2.DoSomething();
ts.Complete();
}
}
主な欠点は、トランザクションが大きくなる可能性があることです。その場合、たとえば、トランザクションブロックでのサービスコールの1つが読み取りのアクセスのみを必要とする場合、ネストでラップします TransactionScope(TransactionScopeOption.Suppress)
トランザクションの寿命において、行/テーブルをさらにロックするのを防ぐためのブロック。