我刚刚开始使用NHibernate,我有一些问题,我不确定如何正确解决。
我开始创建一个包含CUD和一些搜索方法的通用存储库。在DB操作期间,这些方法中的每一个都会打开一个单独的会话(如果需要,还会进行事务处理)。这样做的问题(据我所知)是我无法利用相关集合/对象的延迟加载。
由于几乎每个实体关系在fluent映射中都有.Not.LazyLoad()
,因此当我请求给定类型的所有实体的列表时,它会导致整个数据库被加载。
如果我错了,请纠正我,因为在NHibernate方面,我仍然是一个完整的新手:)
最常见的是要避免这种情况?只要程序运行,或者我应该做什么,就有一个全局静态会话保持活动状态?
一些存储库代码:
public T GetById(int id)
{
using (var session = NHibernateHelper.OpenSession())
{
return session.Get<T>(id);
}
}
使用存储库获取人员
var person = m_PersonRepository.GetById(1); // works fine
var contactInfo = person.ContactInfo; // Throws exception with message:
// failed to lazily initialize a collection, no session or session was closed
答案 0 :(得分:3)
您的问题实际上归结为对象缓存和重用。如果你从一个会话加载一个Foo对象,那么你可以保持它,然后在以后延迟加载它的Bar属性吗?
每个ISession实例都旨在表示一个工作单元,并带有一级缓存,允许您在该工作单元内多次检索对象,但只有一个数据库命中。它不是线程安全的,绝对不能在WinForms应用程序中用作静态对象。
如果要在处理加载对象的会话时使用对象,则需要使用Session.SaveOrUpdate(object)或Session.Update(object)将其与新会话关联。
您可以在chapter 10 of the Hibernate documentation中找到所有这些内容。
如果这看起来效率低下,那么请研究二级缓存。这是在ISessionFactory级别提供的 - 您的会话工厂可以是静态的,如果启用二级缓存,这将有效地构建大部分数据的内存缓存。只有在没有基础服务更新数据的情况下才能进行二级缓存 - 如果所有数据库更新都通过NHibernate进行,那么它是安全的。
根据发布的代码进行修改
您的会话使用情况处于错误的级别 - 您将其用于单个数据库获取,而不是工作单元。在这种情况下,您的GetById
方法应该接受它使用的会话,并且会话实例应该在更高级别进行管理。或者,如果您愿意,您的PersonRepository
类应该管理会话,并且您应该为每个工作单元实例化和处置此类型的对象。
public T GetById(int id)
{
return m_session.Get<T>(id);
}
using (var repository = new PersonRepository())
{
var person = repository.GetById(1);
var contactInfo = person.ContactInfo;
} // make sure the repository Dispose method disposes the session.
您收到的错误消息是因为不再使用会话来延迟加载集合 - 您已经处理了它。