2009-07-09 21 views
8

NHibernateのバージョン:2.1セッションごとのNHリクエスト - 「セッションは終了しました!」

私はASP.NET + NHibernateのアプリケーションで要求ごとのセッションを実装するにはかなりの標準的なHttpModuleをアプローチであると思われるものを使用しています。 WebSessionContextを利用しようとしていますが、正しく動作していないようです。具体的には、アプリケーションの最初のリクエストではすべてがうまく動作しますが、追加のリクエストでは「セッションが閉じられました」という結果になります。セッションが使用されるたびに例外が発生します。アプリケーションプールをリセットすることで、別の要求が成功し、さらに「セッションが閉じられました!」ことができます。

動く部分がいくつかありますが、文脈をどのように細かく管理しているのかは分かりません。ここはすべてです! web.configファイルで

<property name="current_session_context_class"> 
    NHibernate.Context.WebSessionContext, NHibernate 
</property> 

(。私は同じ結果で、あまりにも、単に 'ウェブ' に設定しようとしました)

モジュールは、正しく設定されていることが確認さ:

public class NHibernateSessionModule : IHttpModule 
{ 
    public void Dispose() { } 

    public void Init(HttpApplication context) 
    { 
     Debug.WriteLine("NHibernateSessionModule.Init()"); 
     context.BeginRequest += context_BeginRequest; 
     context.EndRequest += context_EndRequest; 
    } 

    void context_BeginRequest(object sender, EventArgs e) 
    { 
     Debug.WriteLine("NHibernateSessionModule.BeginRequest()"); 
     var session = NHibernateHelper.OpenSession(); 
     session.BeginTransaction(); 
     CurrentSessionContext.Bind(session); 
    } 

    void context_EndRequest(object sender, EventArgs e) 
    { 
     Debug.WriteLine("NHibernateSessionModule.EndRequest()"); 
     var session = NHibernateHelper.GetCurrentSession(); 
     if (session != null) 
     { 
      try 
      { 
       if (session.Transaction != null && session.Transaction.IsActive) 
        session.Transaction.Commit(); 
      } 
      catch (Exception ex) 
      { 
       session.Transaction.Rollback(); 
       throw new ApplicationException("Error committing database transaction", ex); 
      } 
      finally 
      { 
       session.Close(); 
      } 
     } 
     CurrentSessionContext.Unbind(NHibernateHelper.SessionFactory); 
    } 
} 

そして、私の小さなヘルパー:

public class NHibernateHelper 
{ 
    public static readonly ISessionFactory SessionFactory; 

    static NHibernateHelper() 
    { 
     try 
     { 
      Configuration cfg = new Configuration(); 
      cfg.AddAssembly(Assembly.GetCallingAssembly()); 
      SessionFactory = cfg.Configure().BuildSessionFactory(); 
     } 
     catch (Exception ex) 
     { 
      Debug.WriteLine(ex); 
      throw new ApplicationException("NHibernate initialization failed", ex); 
     } 
    } 

    public static ISession GetCurrentSession() 
    { 
     return SessionFactory.GetCurrentSession(); 
    } 

    public static ISession OpenSession() 
    { 
     return SessionFactory.OpenSession(); 
    } 
} 

答えて

2

NHibernate 1.2(NHibernate in Actionから)の例は、アンバインドがクローズの前に行われることを示しています。

この注文変更の変更はありますか?

var session = NHibernateHelper.GetCurrentSession(); 
CurrentSessionContext.Unbind(NHibernateHelper.SessionFactory); 
... 
session.Close(); 
1

次のNHibernateセッションマネージャを使用します。 (これはもともとCodeProjectの記事であり、少し頑強に変更しました).Global.asaxには初期化はなく、web.config/hibernate.xml.cfgの設定パラメータによってのみ初期化されます。

using System.Runtime.Remoting.Messaging; 
using System.Web; 
using NHibernate; 
using NHibernate.Cache; 
using NHibernate.Cfg; 

/// <summary> 
/// Handles creation and management of sessions and transactions. It is a singleton because 
/// building the initial session factory is very expensive. Inspiration for this class came 
/// from Chapter 8 of Hibernate in Action by Bauer and King. Although it is a sealed singleton 
/// you can use TypeMock (http://www.typemock.com) for more flexible testing. 
/// </summary> 
public sealed class NHibernateSessionManager 
{ 
    #region Thread-safe, lazy Singleton 

    /// <summary> 
    /// Gets an instance via a thread-safe, lazy singleton. 
    /// </summary> 
    /// <remarks> 
    /// See http://www.yoda.arachsys.com/csharp/singleton.html for more details about its implementation. 
    /// </remarks> 
    public static NHibernateSessionManager Instance 
    { 
     get 
     { 
      return Nested.NHibernateSessionManager; 
     } 
    } 

    /// <summary> 
    /// Prevents a default instance of the NHibernateSessionManager class from being created. 
    /// Initializes the NHibernate session factory upon instantiation. 
    /// </summary> 
    private NHibernateSessionManager() 
    { 
     this.InitSessionFactory(); 
    } 

    /// <summary> 
    /// Assists with ensuring thread-safe, lazy singleton 
    /// </summary> 
    private class Nested 
    { 
     private Nested() 
     { 
     } 

     internal static readonly NHibernateSessionManager NHibernateSessionManager = new NHibernateSessionManager(); 
    } 

    #endregion 

    private void InitSessionFactory() 
    { 
     this.sessionFactory = new Configuration().Configure().BuildSessionFactory(); 
    } 

    /// <summary> 
    /// Allows you to register an interceptor on a new session. This may not be called if there is already 
    /// an open session attached to the HttpContext. If you have an interceptor to be used, modify 
    /// the HttpModule to call this before calling BeginTransaction(). 
    /// </summary> 
    public static void RegisterInterceptor(IInterceptor interceptor) 
    { 
     ISession session = ContextSession; 

     if (session != null && session.IsOpen) 
     { 
      throw new CacheException(new System.Resources.ResourceManager(typeof(NHibernateSessionManager)).GetString("RegisterInterceptor_CacheException")); 
     } 

     GetSession(interceptor); 
    } 

    /// <summary> 
    /// Gets a session (without an interceptor). This method is not called directly; instead, 
    /// it gets invoked from other public methods. 
    /// </summary> 
    /// <returns></returns> 
    public static ISession GetSession() 
    { 
     return GetSession(null); 
    } 

    /// <summary> 
    /// Gets a session with or without an interceptor. This method is not called directly; instead, 
    /// it gets invoked from other public methods. 
    /// </summary> 
    /// <remarks> 
    /// Throws <see cref="HibernateException"/> if a reference to a session could not be retrieved. 
    /// </remarks> 
    private static ISession GetSession(IInterceptor interceptor) 
    { 
     ISession session = ContextSession; 

     if (session == null) 
     { 
      if (interceptor != null) 
      { 
       session = Instance.sessionFactory.OpenSession(interceptor); 
      } 
      else 
      { 
       session = Instance.sessionFactory.OpenSession(); 
      } 

      ContextSession = session; 
     } 

     if (session == null) 
     { 
      throw new HibernateException("Session was null"); 
     } 

     return session; 
    } 

    /// <summary> 
    /// Flushes anything left in the session, committing changes as long as no <see cref="NHibernate.AssertionFailure">NHibernate.AssertionFailure's</see> are thrown. 
    /// </summary> 
    /// <exception cref="System.Data.SqlClient.SqlException"></exception> 
    public static void FlushSession() 
    { 
     ISession session = ContextSession; 

     if (session != null && session.IsOpen) 
     { 
      // Due to a bug in Hibernate (see http://forum.hibernate.org/viewtopic.php?p=2293664#2293664) make sure Flush() is wrapped in a transaction 
      if (!HasOpenTransaction()) 
      { 
       BeginTransaction(); 
      } 

      try 
      { 
       session.Flush(); 
      } 
      catch (NHibernate.AssertionFailure af) 
      { 
       if (af.Message == "null id in entry (don't flush the Session after an exception occurs)") 
       { 
        System.Diagnostics.Trace.TraceError("NHibernate.AssertionFailure: " + af.Message); 
       } 
       else 
       { 
        throw; 
       } 
      } 
      CommitTransaction(); 
     } 

     ContextSession = null; 
    } 

    /// <summary> 
    /// Flushes anything left in the session and closes the connection. 
    /// </summary> 
    public static void CloseSession() 
    { 
     ISession session = ContextSession; 

     if (session != null && session.IsOpen) 
     { 
      FlushSession(); 
      session.Close(); 
     } 

     ContextSession = null; 
    } 

    /// <summary> 
    /// Begin an ITransaction (if one is not already active) 
    /// </summary> 
    public static void BeginTransaction() 
    { 
     ITransaction transaction = ContextTransaction; 

     if (transaction == null) 
     { 
      transaction = GetSession().BeginTransaction(); 
      ContextTransaction = transaction; 
     } 
    } 

    /// <summary> 
    /// Begin an ITransaction (if one is not already active) 
    /// </summary> 
    /// <param name="isolationLevel"></param> 
    public static void BeginTransaction(System.Data.IsolationLevel isolationLevel) 
    { 
     ITransaction transaction = ContextTransaction; 

     if (transaction == null) 
     { 
      transaction = GetSession().BeginTransaction(isolationLevel); 
      ContextTransaction = transaction; 
     } 
    } 

    /// <summary> 
    /// Commit transaction, if a transaction is currently open. Automatic rollback if commit fails. 
    /// </summary> 
    public static void CommitTransaction() 
    { 
     ITransaction transaction = ContextTransaction; 

     try 
     { 
      if (HasOpenTransaction()) 
      { 
       try 
       { 
        transaction.Commit(); 
       } 
       catch (NHibernate.AssertionFailure af) 
       { 
        if (af.Message == "null id in entry (don't flush the Session after an exception occurs)") 
        { 
         System.Diagnostics.Trace.TraceError("NHibernate.AssertionFailure: " + af.Message); 
        } 
        else 
        { 
         throw; 
        } 
       } 
       ContextTransaction = null; 
      } 
     } 
     catch (HibernateException) 
     { 
      RollbackTransaction(); 
      throw; 
     } 
    } 

    /// <summary> 
    /// Checks for an open <see cref="ITransaction"/>. 
    /// </summary> 
    /// <returns></returns> 
    public static bool HasOpenTransaction() 
    { 
     ITransaction transaction = ContextTransaction; 

     return transaction != null && transaction.IsActive && !transaction.WasCommitted && !transaction.WasRolledBack; 
    } 

    /// <summary> 
    /// Rollback transaction, closing the <see cref="ContextSession"/> if successful. 
    /// </summary> 
    public static void RollbackTransaction() 
    { 
     ITransaction transaction = ContextTransaction; 

     try 
     { 
      if (HasOpenTransaction()) 
      { 
       transaction.Rollback(); 
      } 

      ContextTransaction = null; 
     } 
     finally 
     { 
      if (ContextSession != null) 
      { 
       ContextSession.Close(); 
       ContextSession = null; 
      } 
     } 
    } 

    /// <summary> 
    /// If within a web context, this uses <see cref="HttpContext" /> instead of the WinForms 
    /// specific <see cref="CallContext" />. Discussion concerning this found at 
    /// http://forum.springframework.net/showthread.php?t=572. 
    /// </summary> 
    private static ITransaction ContextTransaction 
    { 
     // this should be here, but it starts a chain of having to mark this ALL over. So we're ignoring it here. 
     // [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.LinkDemand, SkipVerification = true)] 
     get 
     { 
      if (IsInWebContext()) 
      { 
       return (ITransaction)HttpContext.Current.Items[TRANSACTION_KEY]; 
      } 
      else 
      { 
       return (ITransaction)CallContext.GetData(TRANSACTION_KEY); 
      } 
     } 
     // this should be here, but it starts a chain of having to mark this ALL over. So we're ignoring it here. 
     // [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.LinkDemand, SkipVerification = true)] 
     set 
     { 
      if (IsInWebContext()) 
      { 
       HttpContext.Current.Items[TRANSACTION_KEY] = value; 
      } 
      else 
      { 
       CallContext.SetData(TRANSACTION_KEY, value); 
      } 
     } 
    } 

    /// <summary> 
    /// If within a web context, this uses <see cref="HttpContext" /> instead of the WinForms 
    /// specific <see cref="CallContext" />. Discussion concerning this found at 
    /// http://forum.springframework.net/showthread.php?t=572. 
    /// </summary> 
    private static ISession ContextSession 
    { 
     // [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.LinkDemand, SkipVerification = true)] // this should be here, but it starts a chain of having to mark this ALL over. So we're ignoring it here. 
     get 
     { 
      if (IsInWebContext()) 
      { 
       return (ISession)HttpContext.Current.Items[SESSION_KEY]; 
      } 
      else 
      { 
       return (ISession)CallContext.GetData(SESSION_KEY); 
      } 
     } 
     // [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.LinkDemand, SkipVerification = true)] // this should be here, but it starts a chain of having to mark this ALL over. So we're ignoring it here. 
     set 
     { 
      if (IsInWebContext()) 
      { 
       HttpContext.Current.Items[SESSION_KEY] = value; 
      } 
      else 
      { 
       CallContext.SetData(SESSION_KEY, value); 
      } 
     } 
    } 

    private static bool IsInWebContext() 
    { 
     return HttpContext.Current != null; 
    } 

    private const string TRANSACTION_KEY = "CONTEXT_TRANSACTION"; 
    private const string SESSION_KEY = "CONTEXT_SESSION"; 
    private ISessionFactory sessionFactory; 
} 
+0

ありがとうコードです。私は似たようなものを使ってしまった。私はまだ組み込みのWebセッションコンテキストマネージャが動作していないのはなぜか不思議です。コードサンプルの束に使用されています。 – dahlbyk

0
だけで推測

ていますが、最終的には範囲内のあなたのCurrentSessionContext.Unbindを置けば何が起こる、ちょうどsession.Close後の()?私は正確に覚えていないが、finallyブロックが完了した後に実行が終了すると信じているので、セッションがまだコンテキストにバインドされているので、決して追い出されない。

+0

ああ、1分待ってください。セッションはバインドされていますが、SessionFactoryのバインドは解除されています。セッションをバインド解除してはいけませんか? – Siewers

+0

Unbind()は実際には、ISessionFactoryを受け入れ、明らかにバインドされていないSessionを返します。多分私はUnbind()を最初に実行し、次にCommit/Closeを行う必要があります:http://www.bengtbe.com/blog/post/2009/10/08/NerdDinner-with-Fluent-NHibernate-Part-3-インフラストラクチャ.aspx – dahlbyk

関連する問題