代码之家  ›  专栏  ›  技术社区  ›  john84

我的第一个(fluent)nhibernate查询初始化[type]时出错-失败…没有会话或会话被关闭

  •  3
  • john84  · 技术社区  · 15 年前

    我刚开始使用nhibernate,使用fluent nhibernate创建了我的映射,如下所示:

    public class CustomerMap : ClassMap<Customer>
    {
        public CustomerMap()
        {
            Id(x => x._id, "Id");
            Map(x => x._KdNr, "KdNr");
            Map(x => x._Name, "Name");
            HasMany(x => x._Contact)
                .Table("Contacts")
                .KeyColumn("FKCustomerID")
                .LazyLoad();
        }
    }
    
    
    public class ContactMap : ClassMap<Contact>
    {
        public ContactMap()
        {
            Id(x => x._id, "Id");
            Map(x => x._Name, "Name");
        }
    }
    

    为了保存新的记录,工作还包括:

        public static void AddCustomer(Customer cust)
        {
            using (var session = SessionFactory.Instance.OpenSession())
            {
                session.Save(cust);
                session.Save(cust._Contact);
                session.Flush();
            }
        }
    

    然后我尝试选择我添加的客户,使用:

            using (var session = SessionFactory.Instance.OpenSession())
            {
                try
                {
                    var v = session.CreateQuery("from Customer").List<Customers>();
                    return (List<Customer>)v;
                }
                catch
                {
                    session.Close();
                    throw;
                }
                finally
                {
                    session.Disconnect();
                }
            }
        }
    

    客户也加载得很好,但内部的联系人没有引用以下错误:

    初始化[fnh.datamodel.customer d2f2d1c5-7d9e-4f77-8b4f-9e2000 88187b]-失败 要延迟初始化角色集合: fnh.datamodel.kunde.\u联系,没有关闭任何会话或会话

    但我无法理解错误来自何处,因为执行HQL错误后会话将关闭…

    1 回复  |  直到 13 年前
        1
  •  2
  •   UpTheCreek    15 年前

    问题是联系人是延迟加载的,即,在初始查询中不会从数据库中提取集合。我假定您将对象直接传递给视图,而不是使用视图模型?你有几个选择,每个都有缺点。

    1. 在视图中保持会话打开 正在处理(所谓的打开 会话视图方法)。你现在可能正在控制器中关闭NH会话,对吗?

    2. 急切地加载整个对象图 在上使用.not.lazyload()。 联络。( 不推荐 )

    3. 将所需的所有数据复制到视图 控制器中的模型,并通过 这是给视图的。使用automapper帮助您完成此操作。

    更新以响应评论:

    让集合懒散地加载仍然有很大的好处。当然,在这种情况下,您不会从客户对象上的延迟加载联系人中受益,因为您需要使用它们。但在另一个上下文中,您可能只需要客户名称和ID—您可以放心,这不会生成一个带有联接等的大型查询。

    要在视图中使用打开的会话,您不必显式地将会话传递给视图,而只需像以前一样传递对象,但保持会话打开-当您尝试访问联系人集合时,NH将自动生成新的查询。(这是因为客户对象仍然“附加”到后台打开的会话)。这里唯一的区别是,您需要关闭会话,而不是在控制器中(当前正使用.close()显式关闭会话,或使用“using”隐式关闭会话)。关于在哪里打开和关闭会话-有不同的方法:

    • 在global.asax中-open in application_beginrequest和close in application_endrequest(请参见: this article )如果您想在视图中进行开放式会话,为了简单起见,我建议您从这个开始。

    • 在HTTP模块中(基本上与上一个相同,但模块化)(参见 this article )

    对于最后两个,您可能会想,“但这意味着为每个页面请求创建一个会话!”-你是对的,但实际上有多少页不会转到数据库?另外,一旦创建了会话工厂,会话创建就变得轻量级了。

    • 使用一个属性来修饰action方法(您基本上和前两个方法做相同的事情,但是这样您可以选择打开NH会话的操作)。如果希望在操作完成后关闭OnActionExecuted中的会话,请将其关闭。如果要在视图中打开会话,请在OnResultExecuted中关闭。

    当然,许多人不喜欢在视图中打开会话,因为您无法控制纯粹在控制器中生成的查询-视图触发了许多查询,这可能导致不可预测的性能。

    3与2稍有不同,因为在不需要惰性加载集合的情况下,它不会从数据库中提取。例如,在这种情况下,您可能使用完整的对象图复制到一个ViewModel,但在另一种情况下,您可能只使用客户名称和ID的ViewModel—在这种情况下,不会不必要地执行联系人集合的联接。在我看来,3是做事情的正确方式——但是你最终会创建更多的对象(因为视图对象)。