注意:现在我输入了这个,我不得不为超长问题道歉,但是,我认为这里提供的所有代码和信息都是相关的。
好吧,我在ASP.NET webforms应用程序中的随机点上得到了奇怪的“Session Is Closed”错误。然而,今天,它终于在同一个地方一次又一次地发生。我几乎可以确定在我的代码中没有任何处理或关闭会话,因为使用的代码位远远超出了所有其他代码,如下所示。
我也使用ninject作为我的IOC,这可能/可能不重要。
好的,首先是我的SessionFactoryProvider
和SessionProvider
类:
SessionFactoryProvider
public class SessionFactoryProvider : IDisposable
{
ISessionFactory sessionFactory;
public ISessionFactory GetSessionFactory()
{
if (sessionFactory == null)
sessionFactory =
Fluently.Configure()
.Database(
MsSqlConfiguration.MsSql2005.ConnectionString(p =>
p.FromConnectionStringWithKey("QoiSqlConnection")))
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<JobMapping>())
.BuildSessionFactory();
return sessionFactory;
}
public void Dispose()
{
if (sessionFactory != null)
sessionFactory.Dispose();
}
}
SessionProvider
public class SessionProvider : IDisposable
{
ISessionFactory sessionFactory;
ISession session;
public SessionProvider(SessionFactoryProvider sessionFactoryProvider)
{
this.sessionFactory = sessionFactoryProvider.GetSessionFactory();
}
public ISession GetCurrentSession()
{
if (session == null)
session = sessionFactory.OpenSession();
return session;
}
public void Dispose()
{
if (session != null)
{
session.Dispose();
}
}
}
这两个类与Ninject连接如下:
NHibernateModule
public class NHibernateModule : StandardModule
{
public override void Load()
{
Bind<SessionFactoryProvider>().ToSelf().Using<SingletonBehavior>();
Bind<SessionProvider>().ToSelf().Using<OnePerRequestBehavior>();
}
}
据我所知,按预期工作。
现在我的BaseDao<T>
班级:
BaseDao
public class BaseDao<T> : IDao<T> where T : EntityBase
{
private SessionProvider sessionManager;
protected ISession session { get { return sessionManager.GetCurrentSession(); } }
public BaseDao(SessionProvider sessionManager)
{
this.sessionManager = sessionManager;
}
public T GetBy(int id)
{
return session.Get<T>(id);
}
public void Save(T item)
{
using (var transaction = session.BeginTransaction())
{
session.SaveOrUpdate(item);
transaction.Commit();
}
}
public void Delete(T item)
{
using (var transaction = session.BeginTransaction())
{
session.Delete(item);
transaction.Commit();
}
}
public IList<T> GetAll()
{
return session.CreateCriteria<T>().List<T>();
}
public IQueryable<T> Query()
{
return session.Linq<T>();
}
}
在Ninject中绑定的是这样的:
DaoModule
public class DaoModule : StandardModule
{
public override void Load()
{
Bind(typeof(IDao<>)).To(typeof(BaseDao<>))
.Using<OnePerRequestBehavior>();
}
}
现在导致这种情况的Web请求是在我保存对象的时候,直到今天我做了一些模型更改才发生,但是我的模型的更改并没有改变数据访问代码。虽然它改变了一些NHibernate映射(如果有人有兴趣我也可以发布这些映射)
据我所知,BaseDao<SomeClass>.Get
被调用,然后调用BaseDao<SomeOtherClass>.Get
,然后调用BaseDao<TypeImTryingToSave>.Save
。
这是Save()
using (var transaction = session.BeginTransaction())
失败并且“会话已关闭!”或者更确切地说是例外:
Session is closed!
Object name: 'ISession'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ObjectDisposedException: Session is closed!
Object name: 'ISession'.
确实在调试器上显示第三次从SessionProvider
请求会话时,它确实已关闭且未连接。
我已经确认我的Dispose
和SessionFactoryProvider
上的SessionProvider
在请求结束时被调用,而不是在我的Dao上Save
来电之前
所以现在我有点卡住了。想到一些事情。
提前致谢
答案 0 :(得分:19)
ASP.NET是多线程的,因此对ISession的访问必须是线程安全的。假设您正在使用每个请求的会话,最简单的方法是使用NHibernate对contextual sessions的内置处理。
首先配置NHibernate以使用Web会话上下文类:
sessionFactory = Fluently.Configure()
.Database(
MsSqlConfiguration.MsSql2005.ConnectionString(p =>
p.FromConnectionStringWithKey("QoiSqlConnection")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<JobMapping>())
.ExposeConfiguration(x => x.SetProperty("current_session_context_class", "web")
.BuildSessionFactory();
然后使用ISessionFactory.GetCurrentSession()
获取现有会话,或者将新会话绑定到工厂(如果不存在)。下面我将剪切+粘贴我的代码以打开和关闭会话。
public ISession GetContextSession()
{
var factory = GetFactory(); // GetFactory returns an ISessionFactory in my helper class
ISession session;
if (CurrentSessionContext.HasBind(factory))
{
session = factory.GetCurrentSession();
}
else
{
session = factory.OpenSession();
CurrentSessionContext.Bind(session);
}
return session;
}
public void EndContextSession()
{
var factory = GetFactory();
var session = CurrentSessionContext.Unbind(factory);
if (session != null && session.IsOpen)
{
try
{
if (session.Transaction != null && session.Transaction.IsActive)
{
session.Transaction.Rollback();
throw new Exception("Rolling back uncommited NHibernate transaction.");
}
session.Flush();
}
catch (Exception ex)
{
log.Error("SessionKey.EndContextSession", ex);
throw;
}
finally
{
session.Close();
session.Dispose();
}
}
}
答案 1 :(得分:7)
在为我正在处理的Web API项目运行集成测试时,我间歇性地遇到了Session Closed / ObjectDisposedExceptions。
这里没有任何提示解决了它,所以我构建了一个NHibernate的调试版本来了解更多信息,当控制器的Get
函数返回{时,我看到它正在执行linq查询{1}}响应对象。
事实证明,我们的存储库正在执行linq查询并返回域对象的IEnumerable
,但从不调用IEnumerable
来强制查询查询。该服务反过来返回ToList()
,并且Controller将可枚举包装在响应对象中并返回它。
在返回期间,linq查询最终被执行,但NHibernate会话在某个时间点暂时关闭,因此它抛出异常。解决方案是始终确保在返回服务之前在使用会话块中的存储库中调用IEnumerable
。
答案 2 :(得分:3)
我建议你SessionImpl.Close
/ SessionImpl.Dispose
set a breakpoint,看看是谁通过堆栈跟踪调用它。您也可以为自己构建一个NH的调试版本并执行相同的操作。