2011-11-15 13 views
2

NHibernate 3.2に問題があります。nhibernate 3.2 thread_static問題

私は、Javaアプリケーションで使用したソリューションをC#4.0アプリケーションに移植します。 私たちが作成したいのは、NHibernate SessionFactoryを通じたセッションとトランザクションを処理する単純なメカニズムであり、トランザクションは作業単位の初心者によってインスタンス化され、次にすべてのpartecipantメソッドによって使用されます。より大きな作業単位の一部。しかし、これらのサブメソッドを直接呼び出すと、独自のトランザクションを処理します。

these question私はより良いアプローチを説明しました。 まずJavaの世界でやってみましたが、それはかなりうまく動作します。 私はNHibernate 3.2を使って、同じアプローチをc#4.0に移植しています。

すべての私のセッションとトランザクションを処理しようとしているクラスがOperationManager(あなたがUnitOfWorkManagerに考えること)colledされています。私がする必要がある時はいつでもここで

public class OperationManager : IDisposable 
{ 
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); 

    ITransaction tx = null; 
    ISession session = null; 
    bool isInternalTransaction = false; 

    public ISession BeginOperation() 
    { 
     logger.Debug("Thread : " + System.Threading.Thread.CurrentThread.ManagedThreadId); 
     session = PersistenceManager.Istance.GetSession(); 
     if (session.Transaction.IsActive) 
     { 
      isInternalTransaction = false; 
      tx = session.Transaction;        
      logger.Debug(GetCallerClassDotMethod() + " is binding to transaction " + tx.GetHashCode()); 
     } 
     else 
     { 
      isInternalTransaction = true; 
      tx = session.Transaction; 
      tx.Begin(); 
      logger.Debug("Transaction " + tx.GetHashCode() + " created by " + GetCallerClassDotMethod()); 
     } 
     logger.Debug("Session hash " + session.GetHashCode()); 
     return session; 
    } 

    private String GetCallerClassDotMethod() { 
     // needed to get the Business Logic method calling the public methods 
     var st = new StackTrace(); 
     var sf = st.GetFrame(2); 
     var methodReference = sf.GetMethod().Name; 
     var classReference = sf.GetMethod().DeclaringType.FullName; 
     return string.Concat(classReference, ".", methodReference); 
    } 

    public void CommitOperation() 
    { 
     if (isInternalTransaction) 
     {     
      tx.Commit(); 
      logger.Debug(GetCallerClassDotMethod() + " is committing transaction " + tx.GetHashCode()); 
     } 
    } 

    public void RollbackOperation() 
    { 
     if (isInternalTransaction) 
     { 
      tx.Rollback();     
      logger.Debug(GetCallerClassDotMethod() + " is rolling back transaction " + tx.GetHashCode());     
     } 
    } 

    public void Dispose() 
    { 
     tx.Dispose(); 
     session.Dispose(); 
    } 
} 

は、私のPersistenceManager

internal class PersistenceManager : IDisposable 
{ 
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); 
    private static PersistenceManager _istance; 
    private ISessionFactory _SessionFactory; 
    private static Object _lock = new Object(); 

    public static PersistenceManager Istance 
    { 
     get 
     { 
      lock (_lock) 
      { 
       if (_istance == null) 
       { 
        _istance = new PersistenceManager(); 
        logger.Info("New PersistenceManager instance created"); 
       } 
       return _istance; 
      } 
     } 
    } 

    private PersistenceManager() 
    { 
     // Initialize 
     Configuration cfg = new Configuration(); 
     cfg.Configure(ConfigurationManager.ConfigurationManager.Istance.hibernateConfiguration); 
     cfg.SetProperty("connection.connection_string", ConfigurationManager.ConfigurationManager.Istance.connectionString); 

     /* Note: The AddAssembly() method requires that mappings be 
     * contained in hbm.xml files whose BuildAction properties 
     * are set to ‘Embedded Resource’. */ 

     // Add class mappings to configuration object 
     System.Reflection.Assembly thisAssembly = typeof(PersistenceManager).Assembly; 
     cfg.AddAssembly(thisAssembly);    

     // Create session factory from configuration object 
     _SessionFactory = cfg.BuildSessionFactory(); 
    } 



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


    /// <summary> 
    /// Close this Persistence Manager and release all resources (connection pools, etc). It is the responsibility of the application to ensure that there are no open Sessions before calling Close(). 
    /// </summary> 
    public void Close() 
    { 
     _SessionFactory.Close(); 
    } 


    public ISession GetSession() 
    { 
     return _SessionFactory.OpenSession(); 
    } 

} 

ですDBにアクセスするには、OperationManagerインスタンスを使用して現在のセッション(および現在のトランザクション)を取得し、必要に応じて使用します。

public IList<Agency> getAllAgencies() 
    { 
     using (var om = new OperationManager()) 
     { 
      try 
      { 
       om.BeginOperation(); 
       var result = base.Load<Agency>().ToList(); 
       om.CommitOperation(); 
       return result; 
      } 
      catch (Exception ex) 
      { 
       om.RollbackOperation(); 
       throw ex; 
      } 
     } 
    } 

をベースクラスに私はこの問題は、私は<property name="current_session_context_class">thread_static</property>を使用してスレッドごとのモデルで動作するようにNHibernateのセッションファクトリを構成した場合でも、ということである

protected IQueryable<T> Load<T>() where T : Model.ModelEntity 
    { 
     using (var om = new OperationManager()) 
     { 
      try 
      { 
       var session = om.BeginOperation(); 
       var entities = session.Query<T>(); 
       om.CommitOperation(); 
       return entities; 
      } 
      catch (Exception ex) 
      { 
       om.RollbackOperation(); 
       throw new Exception(msg, ex); 
      } 
     } 
    } 

を持っている: 例がここに発見されましたOperationManager.beginOperation()への2回の呼び出しは、異なるセッションeを返すため、異なるトランザクションで返されます。

なぜこれが起こっているのか誰に教えてもらえますか?

編集は: フレディTrebouxの提案に続いて、私はNHibernateはのCurrentSessionContext静的オブジェクトを使用して、新しいセッションを作成するか、単に現在のものを得るメカニズムを実装しようとしました。 残念ながら、これはまだ動作しません。 など、作業単位のトランザクション、セッションに関連するすべてのものを、避け、コードをクリーンアップした後、私は非常に些細なクラスを書いた、と私は

<property name="current_session_context_class">thread_static</property> 

を使用すると、私のSQL Serverとの問題を持っていることを考え出しました2008 db。 そのコンテキストクラス、古典SessionFactory.OpenSession上の()アプローチを使用して、いくつかのデータをロードし、私は次のエラーを取得する:

System.Data.SqlClient.SqlException: A transport-level error has occurred when receiving results from the server. (provider: Shared Memory Provider, error: 0 - Invalid Handle.) 

これが起こる理由を任意のアイデア?

+1

NHでコンテキストセッションを使用する方法の例をご覧ください:http://stackoverflow.com/questions/7454589/no-session-bound-to-the-current-context/7458905#7458905 –

答えて

2

毎回SessionFactory.OpenSession()を呼び出しています。 これは他のものとは関係なく、新しいセッションを開き、返します。

私はこのアプローチを推奨したいとは思っていませんが、動作させるためには、新しいセッションを開くタイミングとリリースするタイミングを知るためにPersistenceManagerで何らかのリファレンスカウントを行う必要があります。

current_session_context_classはこれに影響を与えません。これは、SessionFactory.GetCurrentSession()が返すものだけを制御し、ThreadStaticSessionContext(thread_static)を手動でBind/Unbindを通じて手動で操作する必要があるためです。私は

さて、二つのことは星いくつ

アプローチ。

まず、セッション/トランザクションを処理する特定のレイヤーを定義したいと思います。 私が言っていることは、メソッドAとBがあり、AがBを使うかもしれないがBも外部から使用できるということです.Bのセッション/トランザクション境界を定義するメソッドCが好きです。

したがって、AとCは公開されているメソッドであり、どちらもBを使用しています(Cで直接Bを呼び出している場合でも)。

もう1つは、多くのNHibernateベースのライブラリ(例えばhttp://code.google.com/p/unhaddins/)で見つかるようなセッションコンテキストマネージャを使用する方が良いかもしれないということです。 NHibernateで利用可能なSessionContext機構に合うようにして、SessionFactory.GetCurrentSession()を呼び出してセッションを取得し、同じことを行う他のコンテキスト/メソッドと互換性があるようにします。

+0

私はどのような承認をお勧めしますか尋ねますか?私は最良の方法を見つけようとしているので、どんな提案も評価されます – themarcuz

+0

これはウェブアプリケーションですか? –

+0

そうですね。しかし、私は、ビジネス層がWebアプリケーションで使用されているかどうかには関知しないようにしたい。私はそれをいくつかのWindowsサービスでも使用するつもりです。ですから、有名なセッションごとのリクエストパターンを避けたいのです。 とにかく、私はあなたの提案に続いて、thread_staticアプローチに関連する別のエラーを思いつきました。編集された元の質問 – themarcuz