NHibernate - 延迟初始化角色集合失败

Posted

技术标签:

【中文标题】NHibernate - 延迟初始化角色集合失败【英文标题】:NHibernate -failed to lazily initialize a collection of role 【发布时间】:2010-12-26 00:03:44 【问题描述】:

我有以下看似简单的场景,但是我对 NHibernate 还是很陌生。

尝试为我的控制器上的编辑操作加载以下模型时:

控制器的编辑操作:

public ActionResult Edit(Guid id)

    return View(_repository.GetById(id));

存储库:

public SomeModel GetById(Guid id)

    using (ISession session = NHibernateSessionManager.Instance.GetSession())
        return session.Get<SomeModel >(id);

型号:

public class SomeModel

    public virtual string Content  get; set; 
    public virtual IList<SomeOtherModel> SomeOtherModel  get; set; 

我收到以下错误:

- 延迟初始化角色集合失败:SomeOtherModel,没有会话或会话已关闭

我在这里错过了什么?

【问题讨论】:

【参考方案1】:

问题是您在模型GetById 方法中创建并关闭了会话。 (using语句关闭会话)会话必须在整个业务事务期间可用。

有几种方法可以实现这一点。您可以将 NHibernate 配置为使用会话工厂 GetCurrentSession 方法。请参阅this on nhibernate.info 或this post on Code Project。

public SomeModel GetById(Guid id)

    // no using keyword here, take the session from the manager which
    // manages it as configured
    ISession session = NHibernateSessionManager.Instance.GetSession();
    return session.Get<SomeModel >(id);

我不使用这个。我编写了自己的事务服务,它允许以下内容:

using (TransactionService.CreateTransactionScope())

  // same session is used by any repository
  var entity = xyRepository.Get(id);

  // session still there and allows lazy loading
  entity.Roles.Add(new Role());

  // all changes made in memory a flushed to the db
  TransactionService.Commit();

无论您如何实现它,会话和事务都应该与业务事务(或系统功能)一样长。除非你不能依赖事务隔离也不能回滚整个事情。

【讨论】:

【参考方案2】:

如果您打算在关闭会话之前使用 SomeOtherModel 集合,则需要立即加载它:

using (ISession session = NHibernateSessionManager.Instance.GetSession())

    return session
        .CreateCriteria<SomeModel>()
        .CreateCriteria("SomeOtherModel", JoinType.LeftOuterJoin)
        .Add(Restrictions.Eq(Projections.Id(), id))
        .UniqueResult<SomeModel>();

默认情况下 FluentNHibernate uses lazy loading 用于集合映射。另一种选择是在映射中修改此默认行为:

HasMany(x => x.SomeOtherModel)
    .KeyColumns.Add("key_id").AsBag().Not.LazyLoad();

请注意,如果您这样做,SomeOtherModel 将在您每次加载可能不想要的父实体时被急切地加载(使用外部连接)。一般来说,我更喜欢始终将默认延迟加载保留在映射级别,并根据情况调整我的查询。

【讨论】:

我不会这样做,因为为每个调用打开一个事务是不好的做法。事务隔离也不可用,NHibernate 缓存不再有用(每个调用都返回一个新实例),持久性无知是不可能的,延迟加载不再起作用。简而言之:使用 NHibernate 的大部分优势都被破坏了。【参考方案3】:

“如果我们想要访问订单行项目(在会话关闭后),我们会得到一个异常。由于会话关闭,NHibernate 无法为我们延迟加载订单行项目。我们可以通过以下方式显示此行为测试方法”

[Test]
[ExpectedException(typeof(LazyInitializationException))]
public void Accessing_customer_of_order_after_session_is_closed_throws()

  Order fromDb;
  using (ISession session = SessionFactory.OpenSession())
      fromDb = session.Get<Order>(_order.Id);

  // trying to access the Customer of the order, will throw exception
  // Note: at this point the session is already closed
  string name = fromDb.Customer.CompanyName;

“使用 NHibernateUtil 类急切加载如果您知道需要访问订单实体的相关对象,您可以使用 NHibernateUtil 类来初始化相关对象(即:从数据库中获取它们)。”

[Test]
public void Can_initialize_customer_of_order_with_nhibernate_util()

    Order fromDb;

    using (ISession session = SessionFactory.OpenSession())
    
       fromDb = session.Get<Order>(_order.Id);

       NHibernateUtil.Initialize(fromDb.Customer);
     

    Assert.IsTrue(NHibernateUtil.IsInitialized(fromDb.Customer));
    Assert.IsFalse(NHibernateUtil.IsInitialized(fromDb.OrderLines));


参考:http://nhibernate.info/doc/howto/various/lazy-loading-eager-loading.html

【讨论】:

以上是关于NHibernate - 延迟初始化角色集合失败的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate LazyInitializationException:未能延迟初始化角色集合

无法写入内容:无法延迟初始化角色集合,无法初始化代理 - 无会话

休眠延迟加载不适用于 Spring Boot => 无法延迟初始化角色集合无法初始化代理 - 无会话

无法写入 JSON:无法延迟初始化角色集合

Hibernate:未能延迟初始化角色集合,没有会话或会话被关闭

Hibernate org.hibernate.LazyInitializationException:未能延迟初始化角色集合: