【问题标题】:Problems with HasMany, Lazy loading and session scopesHasMany、延迟加载和会话范围的问题
【发布时间】:2011-03-29 00:00:14
【问题描述】:

我有一个使用 NHibernate 和 ActiveRecord 的控制台应用程序。

我正在使用以下独立标准:

DetachedCriteria criteria = DetachedCriteria.For<ServiceOrder>();

            criteria.Add(Restrictions.Lt("End", DateTime.Today.AddDays(3)));
            criteria.Add(new Disjunction()
                            .Add(Restrictions.IsNull("StopReason"))
                            .Add(Restrictions.Eq("StopReason", ServiceStopReason.Worthiness)));

            criteria.Add(new Disjunction()
                            .Add(Restrictions.IsNull("StopDate"))
                            .Add(Restrictions.EqProperty("StopDate", "End")));

我正在使用 ActiveRecordMediator 的 FindAll() 方法调用获取。
在我的模型中,我有这个属性:

[HasMany(Lazy = true)]
public virtual ISet<ServiceOrder> ServiceOrders
{
    get { return serviceOrders; }
    set { serviceOrders = value; }
}

我试图在以下 Linq 查询中访问:

from serviceOrder in serviceOrderDataService.GetServiceOrdersWithEndOfEntitlement()
     let accountGetService = serviceOrder.AccountGetService
     where accountGetService != null
     let serviceOrders = accountGetService.ServiceOrders
     where serviceOrders != null && serviceOrders.Count != 0
     let isFutureServiceOrder = (from accountGetServiceServiceOrder in serviceOrders
                                      where serviceOrder.Start.HasValue
                                      && accountGetServiceServiceOrder.End.HasValue
                                      && serviceOrder.Start > accountGetServiceServiceOrder.End
                                      select accountGetServiceServiceOrder).Any()
     where !isFutureServiceOrder
     select serviceOrder;

但我得到以下异常:

初始化[Danel.Nursing.Model.AccountService#61786367-e8da-4929-b91b-a7497cf7db10]-失败 懒惰地初始化一个集合 角色: Danel.Nursing.Model.AccountService.ServiceOrders, 没有会话或会话被关闭

这有意义吗?我该如何解决?

【问题讨论】:

  • 既然是怪物查询,能不能具体告诉我们异常是在哪里抛出的?你能提供一个失败的测试用例吗?
  • @MauricioScheffer:一旦我访问 serviceOrders.Count,就会引发异常。关于测试用例,我不确定。超过 50 行。
  • 您如何管理会话范围?
  • @MauricioScheffer:你什么意思?
  • @the_drow:你在哪里/怎么做new SessionScope()?还是您没有明确管理会话范围?

标签: nhibernate session activerecord lazy-loading castle-activerecord


【解决方案1】:

使用延迟加载时,将所有访问延迟加载关系的代码放在一个单个 SessionScope 中。否则你会得到异常(除非你手动将实体重新附加到新会话,这通常很麻烦)。

另一种选择是急切地获取您需要的所有内容(而不是使用延迟加载)。

【讨论】:

  • @the_drow:除非你能提供一个失败的测试用例,否则我们无能为力。
  • 该 linq 查询由于其延迟性质而超出范围执行。谢谢。
【解决方案2】:

这是我在 NHUsers 组 (Link to the discussion, which discusses several other approaches as well) 发的帖子的副本

我知道您有一个控制台应用程序,而我的应用程序是 WinForms,但同样的方法也应该适用于您。这并不理想,但到目前为止,它对我有用。

================================================ =======

使用 NHibernate、延迟加载和 WinForms 时的会话管理 似乎是很多人都在努力解决的问题 - 我知道我做到了!

将 WinForms 数据绑定添加到混合中是一个额外的复杂问题 在我的项目中,它有一个以三分之一为中心的主要形式 方格控制。网格通过数据绑定到我的对象模型 IBindingList 接口。

经过一番挣扎和拔毛,这种方法 似乎正在工作的主要形式是 MainSession 大部分时间都保持打开状态。这意味着每当用户 使用网格浏览数据,有一个打开的会话 处理来自数据库的延迟加载数据。

对于其他操作(更新、删除等),我断开主 会话,仅为该操作创建一个新的一次性会话,并且 然后重新连接主会话。

这很有效,如果它可以帮助其他人,那就太好了。然而,我不 完全信任它,似乎每次我添加新功能(删除 是最新的),它坏了,必须再次调整。如果 谁有更好的建议,我很想听。

相反,如果可以,我也想知道——我会睡得更好 从现在开始。

无论如何,这里是从我的项目中摘录的一些代码来说明 接近。

    /// <summary>
    /// NHibernate session factory used by the application
    /// </summary>
    private static ISessionFactory _sessionFactory;

   /// <summary>
    /// NHibernate session used by MainForm. Kept open most of the time to facilitate
    /// lazy loading of data.  Disconnected and Reconnected when app writes
    /// new data to database
    /// </summary>
    private static ISession _mainSession;

    /// <summary>
    /// Get the main database session, creating it if necessary
    /// </summary>
    /// <returns></returns>
    private static ISession GetMainSession()
    {
        if (_sessionFactory == null)
        {
            CreateSqliteSessionFactory();
        }

        if (_mainSession == null)
        {
            _mainSession = _sessionFactory.OpenSession();
        }

        return _mainSession;
    }

    private static void DisconnectMainSession()
    {
        GetMainSession().Disconnect();
    }

    private static void ReconnectMainSession()
    {
        GetMainSession().Reconnect();
    }

    /// <summary>
    /// Save measurement to DB. Does not check for duplicates
    /// </summary>
    /// <param name="measurement"></param>
    public static void SaveMeasurement(object measurement)
    {
        DisconnectMainSession();

        using (var session = _sessionFactory.OpenSession())
        using (var transaction = session.BeginTransaction())
        {
            try
            {
                session.Save(measurement);

                transaction.Commit();

                session.Close();
            }
            catch (Exception e)
            {
                if (transaction != null) transaction.Rollback();

                throw new ApplicationException(
                    "\r\n    SaveMeasurement exception:\r\n\r\n", e);
            }
            finally
            {
                ReconnectMainSession();
            }
        }
    } 

【讨论】:

  • 静态 ISession 几乎总是一个坏的想法。
  • @Mauricio - 我还听说长时间保持会话打开是一个坏主意 - 因此我需要注意。问题是,我在 SO 和 NHUsers 上不止一次地问过这个问题,但从来没有得到满足我要求的答案,即 WinForms 数据绑定和延迟加载。如果您使用了更好的方法,我很想听听。
  • OP 有一个控制台应用程序,而不是一个 WinForms 应用程序。如果您对 NHibernate+WinForms 有任何疑问,我建议您发布一个新问题。
  • @Mauricio - 重复我在评论中所说的话 - 我已经问过这个问题 - 这是 SO 链接 stackoverflow.com/questions/2567677/…。但那是前一段时间的事了——也许你或其他人现在有更好的答案。请仔细阅读 - 关键是我使用的是 WinForms 数据绑定。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-23
  • 1970-01-01
  • 1970-01-01
  • 2012-08-07
  • 1970-01-01
相关资源
最近更新 更多