【问题标题】:NHibernate -failed to lazily initialize a collection of roleNHibernate - 延迟初始化角色集合失败
【发布时间】: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,没有会话或会话已关闭

我在这里错过了什么?

【问题讨论】:

    标签: asp.net-mvc nhibernate fluent-nhibernate


    【解决方案1】:

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

    有几种方法可以实现这一点。您可以将 NHibernate 配置为使用会话工厂 GetCurrentSession 方法。请参阅this on nhibernate.infothis 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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-04-01
        • 2015-05-03
        • 2019-08-06
        • 2017-08-16
        • 1970-01-01
        • 1970-01-01
        • 2017-08-13
        相关资源
        最近更新 更多