2009-07-09 32 views
8

NHibernate Version: 2,1NH Yêu cầu mỗi phiên - "Phiên bị đóng!"

Tôi đang sử dụng những gì có vẻ là một cách tiếp cận khá HttpModule tiêu chuẩn để thực hiện phiên mỗi yêu cầu trong một ứng dụng ASP.NET + NHibernate. Tôi đang cố gắng tận dụng WebSessionContext, nhưng nó dường như không hoạt động chính xác. Cụ thể, mọi thứ hoạt động xuất sắc cho yêu cầu đầu tiên trên ứng dụng, nhưng yêu cầu bổ sung dẫn đến "Phiên bị đóng!" ngoại lệ bất kỳ lúc nào phiên được sử dụng. Đặt lại nhóm ứng dụng cho phép yêu cầu khác thành công, sau đó thêm "Phiên được đóng!".

Có một vài mẩu di chuyển, nhưng tôi không biết đủ về cách bối cảnh được quản lý để thu hẹp nó xuống vì vậy ... đây là tất cả mọi thứ!

Trong web.config:

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

(Tôi đã thử thiết lập nó để chỉ 'web' cũng vậy, với kết quả tương tự.)

Module, khẳng định được cấu hình một cách chính xác:

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); 
    } 
} 

Và helper nhỏ của tôi:

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(); 
    } 
} 

Trả lời

2

Ví dụ về NHibernate 1.2 (từ NHibernate in Action), cho thấy rằng unbind được thực hiện trước khi đóng.

Thay đổi đặt hàng này có giúp được không?

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

Tôi sử dụng trình quản lý phiên NHibernate sau đây. (Điều này đã được ban đầu từ một bài viết CodeProject, mà tôi đã sửa đổi để được một chút mạnh mẽ hơn.) Không có khởi tạo trong Global.asax, chỉ thông qua các tham số cấu hình trong 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

Cảm ơn mã. Tôi đã sử dụng một cái gì đó tương tự. Tôi vẫn tò mò tại sao trình quản lý ngữ cảnh phiên bản web được tích hợp không hoạt động, vì nó được sử dụng trong một loạt các mẫu mã. – dahlbyk

0

Chỉ cần đoán, nhưng điều gì xảy ra nếu bạn đặt CurrentSessionContext.Unbind bên trong phạm vi cuối cùng, ngay sau session.Close()? Tôi không thể nhớ chính xác, nhưng tôi tin rằng việc thực hiện được kết thúc sau khi khối cuối cùng được thực hiện, do đó, nếu đó là trường hợp, phiên vẫn còn bị ràng buộc vào bối cảnh, và do đó không bao giờ bị trục xuất ..?

+0

Ồ, đợi một chút. Bạn đang ràng buộc một ISession nhưng không ràng buộc SessionFactory. Bạn có nên hủy liên kết phiên không? – Siewers

+0

Unbind() thực sự chấp nhận một ISessionFactory, và dường như trả về ISession không bị ràng buộc. Có lẽ tôi cần Unbind() trước và sau đó Commit/Close, như được hiển thị ở đây: http://www.bengtbe.com/blog/post/2009/10/08/NerdDinner-with-Fluent-NHibernate-Part-3- The-infrastructure.aspx – dahlbyk

Các vấn đề liên quan