2

我在 win 服务中使用 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;
        }

线程池回调:

        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)
            {
                //....
            }
        }
4

2 回答 2

4

有一些潜在的问题,但我怀疑最大的问题是您将任务保存在一个会话中(因此该任务的吸引力与会话 1 相关联),然后将任务保存在另一个会话中 - 所以您以会话 1 中的 Attraction 结束,该会话现已关闭,会话 2 正在导航关系以查看是否需要保存 Attraction 并因此出现错误。这是一个假设,因为我没有您的模型或映射。

我认为最简单的解决方法是在您的回调中打开会话,即:

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();
    }
}
于 2012-06-21T17:35:04.357 回答
0

您好 Andrew,如果您使用 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

}

我的休眠助手:

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

希望对您有所帮助,我很乐意为您提供帮助。

于 2012-06-20T17:23:26.857 回答