我在胜利服务中使用NHiberante。有时我会
System.ObjectDisposedException: Session is closed!
Object name: 'ISession'.
at NHibernate.Impl.AbstractSessionImpl.ErrorIfClosed()
at NHibernate.Impl.AbstractSessionImpl.CheckAndUpdateSessionStatus()
at NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.Save(Object obj)
at Attraction.DAL.Repositories.Repository`1.Save(T entity)
at Attraction.VideoDispatcher.Program.ThreadPoolCallback(Object threadContext)
我不知道出了什么问题。 我的会话管理子系统:
存储库:
public class Repository<T> : IRepository<T>, IDisposable
{
protected readonly bool CommitAtDispose;
public Repository(bool commitAtDispose)
{
CommitAtDispose = commitAtDispose;
StartSession();
}
private void StartSession()
{
if (NHibernateSession == null)
NHibernateHelper.StartSession();
}
public void Dispose()
{
if (CommitAtDispose)
Flush();
}
public void Flush()
{
NHibernateHelper.EndSession();
}
protected override sealed ISession NHibernateSession
{
get
{
return SessionManager.CurrentSession;
}
}
public virtual T GetById(int id)
public virtual List<T> GetAll()
public virtual List<T> GetByPage(int pageIndex, int pageSize)
public virtual int GetCount()
public virtual List<T> GetByCriteria(params ICriterion[] criterion)
public virtual T Save(T entity)
public virtual T Update(T entity)
public virtual void Delete(T entity)
}
}
SessionManager - 单一用于提供对sessionfactory的访问
public class SessionManager : ISessionFactoryProvider
{
private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly ISessionFactory sessionFactory;
public static ISessionFactory SessionFactory
{
get { return Instance.sessionFactory; }
}
public ISessionFactory GetSessionFactory()
{
return sessionFactory;
}
public static ISession OpenSession()
{
return Instance.GetSessionFactory().OpenSession();
}
public static ISession CurrentSession
{
get
{
if (!CurrentSessionContext.HasBind(Instance.GetSessionFactory()))
return null;
return Instance.GetSessionFactory().GetCurrentSession();
}
}
public static SessionManager Instance
{
get
{
return NestedSessionManager.sessionManager;
}
}
private SessionManager()
{
Log.Info("Start creating factory");
Configuration configuration = new Configuration().Configure();
sessionFactory = configuration.BuildSessionFactory();
Log.Info("End creating factory");
}
class NestedSessionManager
{
internal static readonly SessionManager sessionManager =
new SessionManager();
}
}
NhibernateHelper,它为开始和结束会话做了一些工作:
public static class NHibernateHelper
{
public static void StartSession()
{
var session = SessionManager.SessionFactory.OpenSession();
session.BeginTransaction();
CurrentSessionContext.Bind(session);
}
public static void EndSession()
{
var session = SessionManager.CurrentSession;
CurrentSessionContext.Unbind(SessionManager.SessionFactory);
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.Message, ex);
}
finally
{
session.Close();
session.Dispose();
}
}
}
}
可能是我的设计不太好,但我无法想象如何才能发现这个错误。
UPD
抱歉我的配置。我还没有移植到流利,所以:
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MySQLDialect</property>
<property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
<property name="connection.connection_string">***</property>
<property name="show_sql">false</property>
<property name="default_schema">**_***</property>
<property name="current_session_context_class">thread_static</property>
<mapping assembly="***.Core"/>
</session-factory>
</hibernate-configuration>
UPD2
保存方法:
public virtual T Save(T entity)
{
NHibernateSession.Save(entity);
return entity;
}
Threadpool回调:
public static void DetectStart(Object threadContext)
{
try
{
var task = (TasksPerAttraction)threadContext;
var startInfo = new ProcessStartInfo(..., ...)
{
UseShellExecute = false,
RedirectStandardOutput = true
};
Process p = Process.Start(startInfo);
var outputXml = p.StandardOutput.ReadToEnd();
p.WaitForExit();
var doc = XDocument.Parse(outputXml);
foreach (var xElement in doc.Root.Descendants("start"))
{
var startDetection = new StartDetection
{
DtStart = DateTime.Parse(xElement.Attribute("startTime").Value),
Attraction = task.Attraction,
};
lock (spinLock)
{
using (var repo = new Repository<StartDetection>(true))
repo.Save(startDetection);
}
}
var tskRepo = new Repository<Task>(true);
foreach(var tsk in task.Tasks)
{
tsk.IsProcessedStart = true;
tskRepo.Update(tsk);
}
tskRepo.Flush();
}
catch (Exception ex)
{
//....
}
}
答案 0 :(得分:4)
有一些潜在的问题,但我怀疑最大的问题是你正在保存任务。一个会话中的吸引力(因此该任务的吸引力与会话1相关联),然后将任务保存在另一个会话中 - 所以你最终得到会话1中的吸引力,它现在已经关闭,会话2正在导航关系以查看它是否需要保存吸引力并因此保存错误。这是一个假设,因为我没有你的模型或映射。
我认为最简单的解决方法是在回调中打开会话,即:
public static void DetectStart(Object threadContext)
{
try
{
... run your process ...
p.WaitForExit();
// **** OPEN SESSION HERE ****
NHibernateHelper.StartSession();
var doc = XDocument.Parse(outputXml);
foreach (var xElement in doc.Root.Descendants("start"))
{
var startDetection = new StartDetection
{
DtStart = DateTime.Parse(xElement.Attribute("startTime").Value),
Attraction = task.Attraction,
};
lock (spinLock)
{
// *** DON'T CLOSE THE SESSION ON DISPOSE
using (var repo = new Repository<StartDetection>(false))
repo.Save(startDetection);
}
}
// *** DON'T CLOSE THE SESSION ON DISPOSE HERE EITHER!
using(var tskRepo = new Repository<Task>(false))
{
foreach(var tsk in task.Tasks)
{
tsk.IsProcessedStart = true;
tskRepo.Update(tsk);
}
tskRepo.Flush();
}
}
catch (Exception ex)
{
//....
}
finally {
// *** MAKE SURE YOU CLOSE THE SESSION
NHibernateHelper.EndSession();
}
}
答案 1 :(得分:0)
你好安德鲁,如果你使用IOC来处理你的会话怎么办?我使用ninject并且它已经无痛我也将分享一些可能对你有帮助的代码,这是我对Nhibernate处理的近似,但我愿意接受建议。
回购类:
public class Repositorio<T> : IRepositorio<T> where T : class
{
[Inject]
public ISession Session { get; set; }
#region IRepositorio<T> Members
public IList<T> ListAll()
{
return Session.CreateCriteria<T>().List<T>();
}
public T Look(object id)
{
return Session.Get<T>(id);
}
public void Add(T t)
{
using (ITransaction transaction = Session.BeginTransaction())
{
transaction.Begin();
try
{
Session.Save(t);
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
finally
{
transaction.Dispose();
}
}
}
public void Save(T t)
{
using (ITransaction transaction = Session.BeginTransaction())
{
transaction.Begin();
try
{
Session.SaveOrUpdate(t);
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
}
finally
{
transaction.Dispose();
}
}
}
public void Delete(T t)
{
using (ITransaction transaction = Session.BeginTransaction())
{
transaction.Begin();
try
{
Session.Delete(t);
transaction.Commit();
}
catch (Exception e)
{
transaction.Rollback();
Console.WriteLine(e.StackTrace);
}
finally
{
transaction.Dispose();
}
}
}
#endregion
}
我的Nhibernate助手:
public sealed class NHibernateHelper
{
public static ISessionFactory SessionFactory { get; set; }
private static void OpenSession()
{
var configuration = new Configuration();
configuration.Configure();
SessionFactory = configuration.BuildSessionFactory();
}
public static ISession GetCurrentSession()
{
if (SessionFactory == null)
{
OpenSession();
}
if (SessionFactory != null)
return SessionFactory.OpenSession();
return null;
}
public static IStatelessSession GetStatelessSession()
{
if (SessionFactory == null)
{
OpenSession();
}
if (SessionFactory != null)
return SessionFactory.OpenStatelessSession();
return null;
}
public static void CloseSessionFactory()
{
if (SessionFactory != null)
SessionFactory.Close();
}
}
在我的模块中,我这样做:
如何将会话绑定到回购:
Bind<ISession>().ToMethod(c => NHibernateHelper.GetCurrentSession()).InSingletonScope().OnDeactivation(c => NHibernateHelper.CloseSessionFactory());
注意:检查绑定的范围可能对你来说更适合于线程范围
如何将Open会话与repo绑定
Bind<SomeRepoImpl>().ToSelf();
希望有这个帮助,我很乐意帮助你。