Asp.net NH每个会话的请求-“;会议结束了&引用;
NHibernate版本:2.1 在ASP.NET+NHibernate应用程序中,我正在使用一种似乎相当标准的HttpModule方法来实现每请求会话。我试图利用Asp.net NH每个会话的请求-“;会议结束了&引用;,asp.net,nhibernate,Asp.net,Nhibernate,NHibernate版本:2.1 在ASP.NET+NHibernate应用程序中,我正在使用一种似乎相当标准的HttpModule方法来实现每请求会话。我试图利用WebSessionContext,但它似乎工作不正常。具体来说,对于应用程序上的第一个请求,所有操作都非常出色,但是任何时候使用会话时,额外的请求都会导致“会话已关闭!”异常。重置应用程序池允许另一个请求成功,然后更多的“会话已关闭!” 这里有一些移动的片段,但我不太了解上下文是如何缩小范围的,所以…这里是所有内容 在web.con
WebSessionContext
,但它似乎工作不正常。具体来说,对于应用程序上的第一个请求,所有操作都非常出色,但是任何时候使用会话时,额外的请求都会导致“会话已关闭!”异常。重置应用程序池允许另一个请求成功,然后更多的“会话已关闭!”
这里有一些移动的片段,但我不太了解上下文是如何缩小范围的,所以…这里是所有内容
在web.config中:
<property name="current_session_context_class">
NHibernate.Context.WebSessionContext, NHibernate
</property>
还有我的小助手:
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();
}
}
我使用以下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;
}
使用System.Runtime.Remoting.Messaging;
使用System.Web;
使用NHibernate;
使用NHibernate.Cache;
使用NHibernate.Cfg;
///
///处理会话和事务的创建和管理。这是一个单身汉,因为
///构建初始会话工厂非常昂贵。这门课的灵感来了
///出自鲍尔和金的《冬眠行动》第八章。虽然它是一个封闭的单身汉
///您可以使用TypeMock(http://www.typemock.com)用于更灵活的测试。
///
公共密封类NHibernateSessionManager
{
#区域线程安全、懒惰的单例
///
///通过线程安全的惰性单例获取实例。
///
///
///看http://www.yoda.arachsys.com/csharp/singleton.html 有关其实现的更多详细信息。
///
公共静态NHibernateSessionManager实例
{
得到
{
返回Nested.NHibernateSessionManager;
}
}
///
///阻止创建NHibernateSessionManager类的默认实例。
///实例化时初始化NHibernate会话工厂。
///
私人NHibernateSessionManager()
{
这是.InitSessionFactory();
}
///
///协助确保线程安全、懒惰的单例
///
私有类嵌套
{
私有嵌套()
{
}
内部静态只读NHibernateSessionManager NHibernateSessionManager=新的NHibernateSessionManager();
}
#端区
私有void InitSessionFactory()
{
this.sessionFactory=新配置().Configure().BuildSessionFactory();
}
///
///允许您在新会话上注册拦截器。如果已有拦截器,则可能不会调用该拦截器
///连接到HttpContext的打开会话。如果要使用拦截器,请修改
///在调用BeginTransaction()之前调用此函数的HttpModule。
///
公共静态无效寄存器侦听器(侦听器侦听器)
{
ISession会话=上下文会话;
if(session!=null&&session.IsOpen)
{
抛出新的CacheException(new System.Resources.ResourceManager(typeof(NHibernateSessionManager)).GetString(“RegisterInterceptor_CacheException”);
}
GetSession(拦截器);
}
///
///获取会话(不带拦截器)。此方法不是直接调用的,而是,
///它从其他公共方法调用。
///
///
公共静态会话GetSession()
{
返回GetSession(null);
}
///
///获取包含或不包含拦截器的会话。此方法不是直接调用的,而是,
///它从其他公共方法调用。
///
///
///如果无法检索对会话的引用,则引发。
///
专用静态ISession GetSession(IInterceptor侦听器)
{
ISession会话=上下文会话;
if(会话==null)
{
if(拦截器!=null)
{
session=Instance.sessionFactory.OpenSession(拦截器);
}
其他的
{
session=Instance.sessionFactory.OpenSession();
}
上下文会话=会话;
}
if(会话==null)
{
抛出新的HibernateeException(“会话为空”);
}
返回会议;
}
///
///刷新会话中剩余的任何内容,只要没有引发NHibernate.AssertionFailure,就提交更改。
///
///
公共静态void FlushSession()
{
ISession会话=上下文会话;
if(session!=null&&session.IsOpen)
{
//由于Hibernate中的错误(请参阅http://forum.hibernate.org/viewtopic.php?p=2293664#2293664)确保在事务中包装Flush()
如果(!HasOpenTransaction())
{
BeginTransaction();
}
尝试
{
session.Flush();
}
catch(NHibernate.AssertionFailure af)
{
if(af.Message==“条目中的null id(发生异常后不要刷新会话)”)
{
System.Diagnostics.Trace.TraceError(“NHibernate.AssertionFailure:”+af.Message);
}
其他的
{
投掷;
}
}
提交交易();
}
ContextSession=null;
}
///
///刷新会话中剩余的任何内容并关闭连接。
///
公共静态会话()
{
ISession会话=上下文会话;
if(session!=null&&session.IsOpen)
{
FlushSession();
session.Close();
}
ContextSession=null;
}
///
///开始一个ITransaction(如果没有
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;
}
var session = NHibernateHelper.GetCurrentSession();
CurrentSessionContext.Unbind(NHibernateHelper.SessionFactory);
...
session.Close();