2012-03-21 2 views
6

NHibernate永続化層のために私自身のIUnitOfWorkの実装を作ることを考えています。nhibernateで1つのセッション内で複数のトランザクションを実行できますか?それは悪い考えですか?

これを行う正しい方法は、ISessionITransactionをコンストラクタでインスタンス化し、デストラクタまたはDispose()メソッドに配置することです。もちろん

、誰かがSave()メソッドを呼び出す場合、ISessionがフラッシュされるだろうとSave()を呼び出した後、Save()に有効なオープン取引がありませんので、ITransactionは再び、完全だろう...私は約束しない限り、直ちに別の新しい取引を開始しました。しかしこれは良い考えですか?

設計上、1つのコミット操作を行うのは理にかなっていますが、必ずしもコードを管理する必要はなく、他の開発者はUnitOfWorkパターンに従うことについてあまり訓練されません。

セッションごとに複数のトランザクションに対してUnitOfWorkが耐性を持てるようにして、何かを失う/獲得することはありますか?オープンなトランザクションをチェックして、新しいトランザクションを作成するのではなく、既にコミットされている場合は例外をスローする必要がありますか?

+0

ウェブまたはデスクトップアプリ? – Phill

+0

これはWebアプリケーション用に設計されています。 –

答えて

11

最初の質問に答えるには:はい1つのセッションで複数のトランザクションを持つことは可能です。

いいですか?場合によります。

問題は、最初のトランザクションでデータを変更するとコミットが行われ、作業単位(セッション)全体が最後にコミットされるかどうかは不明です。後のトランザクションでStaleObjectExceptionが発生した場合、すでにいくつかのデータがコミットされています。この種の例外はあなたのセッションを使用できなくし、とにかくそれを破壊しなければならないことに注意してください。その後、やり直してやり直すのは難しいです。

私はそれがこのような状況の下でうまく機能し、言う:

  • それは
  • 変更は、最後のトランザクションでフラッシュされたUIアプリケーションです。

UIアプリケーション

エラーがインタラクティブにユーザによって処理されます。これは、ユーザーがエラーの場合に実際に格納されているものを確認し、彼が行った変更を繰り返すことを意味します。 NHによって実装される

変更は最後のトランザクションでセッション

だけフラッシュされるだけで終わりに変更または「必要な時」をフラッシュします。したがって、セッションがコミットされるまで、メモリ内の変更を保持することは可能です。問題はNHがすべてのクエリの前にセッションをフラッシュする必要があることです。これは制御が困難です。それは、副作用につながる、オフにすることができます。単純なトランザクションを記述するときは、そのトランザクションを制御することができます。複雑なシステムでは、何も間違っていないことを確認することは事実上不可能です。

簡単(TM)

は、私はかなり大規模なクライアント・サーバシステムの永続化層を書きました。このようなシステムでは、ユーザーがエラーを直接処理することはありません。システム内のエラーを処理し、一貫した状態でクライアントに制御を戻す必要があります。

トランザクションの処理全体を最小限にすることで、安定した「馬鹿証明」を実現しました。私はいつも一緒にセッションとトランザクションを作成しており、コミットされたかどうかはわかります。

+0

私の理解は、 'ITransaction.Commit()'呼び出しがセッションをフラッシュすることです。その同じセッションを使用して新しい 'ITransaction'をインスタンス化し、' Commit() 'の直後に、どのような状況で' StaleObjectException'が生成されますか? –

+0

トランザクションをコミットするとロックが解除されます。これにより、StaleObjectExceptionsを取得する可能性が高くなります。ただし、メモリ内でデータを変更している間は、データベース内のデータが変更される可能性があります。 NHの楽観的なロックは、これらの競合を少なくとも検出することを可能にする。 –

+1

詳細な回答ありがとうございます。 –

2

作業ユニットでnhibernateネスト・トランザクションを実装するための複数のオプションがあります。

ここでは、作業単位にコマンドパターンを使用しています。

public interface IAction 
{  
    void Execute(); 
} 

public abstract class Action<T> : IAction, IDisposable where T : Action<T> 
{ 
    public void Execute() 
    { 
     try 
     { 
      //Start unit of work by your unit of work pattern or 
      transaction.Begin(); 
      OnExecute(); 
      //End Unit of work 
      transaction.Commit(); 
     } 
     catch (Exception e) 
     { 
      transaction.Rollback(); 
      throw e; 
     } 
    } 

    protected abstract void OnExecute(); 

    public void Dispose() 
    { 

    } 
} 

public class MyBusinessLogic : Action<MyBusinessLogic> 
{ 
    protected override void OnExecute() 
    { 
     //Implementation 
    } 
} 

public class MyAnotherBusinessLogic : Action<MyAnotherBusinessLogic> 
{ 
    protected override void OnExecute() 
    { 
     //Nested transaction 
     MyBusinessLogic logic = new MyBusinessLogic(); 
     logic.Execute(); 
    } 
} 
+0

ちょっとしたことを指摘するには:1)既にSystem.Action があるので、名前が混乱することがあります。 2)あなたの行動クラスは、トランザクションがどこから来るのかを定義しません。 3)これは入れ子になったトランザクションがNHibernateで既に発生しているのと同じエラーを引き起こします(内部トランザクションをコミットすると、外部トランザクションはObjectDisposedExceptionをスローします)。 – rossisdead

0

私は、1つの作業単位あたり1つのトランザクションを持つソリューションはあまりにも制限的だと思います。環境によっては、セッションごとに複数のトランザクションを実行する能力が必要な場合があります。私自身がトランザクションを明示的に管理しており、柔軟な解決策と思われます。

public interface IUnitOfWork: IDisposable 
{ 
    IGenericTransaction BeginTransaction(); 
} 

public interface IGenericTransaction: IDisposable 
{ 
    void Commit(); 

    void Rollback(); 
} 

public class NhUnitOfWork: IUnitOfWork 
{ 
    private readonly ISession _session; 

    public ISession Session 
    { 
     get { return _session; } 
    } 

    public NhUnitOfWork(ISession session) 
    { 
     _session = session; 
    } 

    public IGenericTransaction BeginTransaction() 
    { 
     return new NhTransaction(_session.BeginTransaction()); 
    } 

    public void Dispose() 
    { 
     _session.Dispose(); 
    } 
} 

public class NhTransaction: IGenericTransaction 
{ 
    private readonly ITransaction _transaction; 

    public NhTransaction(ITransaction transaction) 
    { 
     _transaction = transaction; 
    } 

    public void Commit() 
    { 
     _transaction.Commit(); 
    } 

    public void Rollback() 
    { 
     _transaction.Rollback(); 
    } 

    public void Dispose() 
    { 
     _transaction.Dispose(); 
    } 
} 

使用方法は次のようになります。どのパターンにも簡単に組み込むことができます。

public void Do(IUnitOfWork uow) 
{ 
    using (var tx = uow.BeginTransaction()) { 
    // DAL calls 
    tx.Commit(); 
    } 
} 
+0

これはまさに私たちがやっていることですが、この場合はうまくいきません。 コメントでコード編集を実行できません... そこに別のトランザクションがあると動作しません。しかしそれはすべきです。ネストされたトランザクションは、正当な理由でデータベースに実装されます。 – user2415376

関連する問題