如何正确使用NHibernate ISession对象 - 会话已关闭!错误

时间:2009-09-24 17:14:08

标签: nhibernate design-patterns

我在NHibernate中遇到了我的ISessions问题。我一直在“会议结束!”错误。有人可以告诉我正确的模式,包括以下方法的定义以及何时使用每种方法:

ISession.Close()
ISession.Dispose()
ISession.Disconnect()

这是我的问题。我有一个回调设置来启动一个每隔几分钟向玩家颁发徽章的流程。但是我一直在“会议结束!”关于无法关联馆藏的错误或错误。

这是我的存储库:

public class NHibernateRepository : IRepository
{
#region Fields

private ISession _session;
private readonly ISessionFactory _sessionFactory;
#endregion

#region Constructors

public NHibernateRepository(ISessionFactory sessionFactory)
{
    _sessionFactory = sessionFactory;
}

#endregion

#region IRepository Implementation

public ISession OpenSession()
{
    _session = _sessionFactory.OpenSession();
    return _session;
}

public IQueryable<TModel> All<TModel>()
{
    return _session.Linq<TModel>();
}

public void Save<TModel>(TModel model)
{
    _session.Save(model);
}
public void Update<TModel>(TModel model)
{
    _session.Update(model);
}
public void Delete<TModel>(TModel model)
{
    _session.Delete(model);
}

public ITransaction BeginTransaction()
{
    return _session.BeginTransaction();
}
public void Flush()
{
    _session.Flush();
}
#endregion

}

这是我的用法。存储库通过Structure Map

注入
private Object _awardBadgesLock = new object(); //In case the callback happens again before the previous one completes

public void AwardBadges()
{

    lock (_awardBadgesLock)
    {
        using(session = _repository.OpenSession())
        {
            foreach (var user in _repository.All<User>().ToList())
            {
                var userPuzzles = _repository.All<Puzzle>().ByUser(user.Id).ToList();
                var userVotes = _repository.All<Vote>().Where(x => x.UserId == user.Id).ToList();
                var userSolutions = _repository.All<Solution>().ByUser(user.Id).ToList().Where(x => !userPuzzles.Select(y => y.Id).Contains(x.PuzzleId));
                var ledPuzzles = GetPuzzlesLedByUser(user.Id);

                AwardPlayerBadge(user, userSolutions);
                AwardCriticBadge(user, userVotes);
                AwardCreatorBadge(user, userPuzzles);
                AwardRidlerBadge(user, userPuzzles);
                AwardSupporterBadge(user, userVotes);
                AwardPopularBadge(user, userPuzzles);
                AwardNotableBadge(user, userPuzzles);
                AwardFamousBadge(user, userPuzzles);
                AwardLeaderBadge(user, ledPuzzles);

                using (var tx = _repository.BeginTransaction())
                {
                    _repository.Update(user);
                    tx.Commit();
                }
            }
        }
    }

}

4 个答案:

答案 0 :(得分:13)

你应该总是使用session.Dispose(); 另一个是非常奇怪的出现

答案 1 :(得分:6)

我建议你阅读ISession的文档 https://nhibernate.svn.sourceforge.net/svnroot/nhibernate/trunk/nhibernate/src/NHibernate/ISession.cs

无论如何,当你完成会话时清理的正确方法是处理它(或者更好,用using语句包围使用)。在这种情况下,“使用”会关闭会话并抑制终结器,即它会阻止会话对象不必要地幸免于下一次垃圾收集并保存内存。

如果连接已经关闭,处理它不会引发异常。另一方面,处理后(或关闭后)关闭会引发异常。

文档建议调用disconnect而不是关闭,因为这会释放与连接池的连接。您应该在使用断开连接的会话之前调用重新连接。

根据我的需要,我总是使用“使用”调用Dispose并且从未使用过其他两种功能。

答案 2 :(得分:1)

问题在于ISession不是线程安全的。在单独的线程上触发了多个方法,这些方法都创建了一个ISession实例。问题实际上是他们都共享相同的SessionFactory。图像这两种方法都是在不同的线程上启动的:

ISessionFactory _sessionFactory;

void MethodOne()
{
   using(ISession session = _sessionFactory.OpenSession()) 
   {
       //Do something with really quick with the session
       //Then dispose of it
   }
}

void MethodTwo()
{
   //OpenSession() actually returns the same instance used in the 
   //previous method that has already disposed of the object;
   using(ISession session = _sessionFactory.OpenSession()) 
   {
       //Do something with a session that has already been disposed
       //throws errors

   }
}

我如何修复它基本上是在这些场景中抛弃NHIbernate并改为调用存储过程。无论如何,我觉得它在我的情况下表现得更好。

答案 3 :(得分:0)

关于这个问题,你的锁定方法是正确的,只要你处理会话,但可能是你的代码的另一部分。顺便说一下,关于设计,最好将会话变量传递给存储库,因为会话的工作单元实现和聚合根的事务如下:

using (ISession session = SessionFactory.OpenSession())
{
    Repository1 rep1 = new Repository1(session);
    Repository2 rep1 = new Repository2(session);
    Repository3 rep1 = new Repository3(session);

    // some logics

    using (var tx = session.BeginTransaction())
        tx.Commit();
}

。 。