我的第一个(流畅的)nhibernate查询中出现错误初始化[type] -failed ...没有会话或会话被关闭

时间:2010-10-31 08:07:48

标签: nhibernate fluent

我刚刚开始使用NHibernate,使用流畅的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-9e200088187b] -failed   懒惰地初始化角色集合:   fnh.DataModel.Kunde._Contact,没有关闭会话或会话

但我无法理解错误的来源,因为会话在执行我的HQL错误后关闭...

1 个答案:

答案 0 :(得分:2)

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

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

  2. 急切加载整个对象图 使用.Not.Lazyload()就可以了 联系人。 (不推荐

  3. 将您需要的所有数据复制到视图中 控制器中的模型,并通过 这个看法。使用automapper来帮助您。

  4. 更新以回复评论:

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

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

    • 在global.asax中 - 在Application_BeginRequest中打开并在Application_EndRequest中关闭(参见:this article)为了简单起见,如果你想在View中进行Open Session,我建议从这开始。

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

    对于最后两个,你可能会想'但这意味着为每个页面请求创建一个会话!' - 你会是对的,但实际上有多少页不会进入数据库?创建会话工厂后,加上会话创建是轻量级的。

    • 使用一个属性来装饰动作方法(你基本上和最后两个一样做,但这样你可以选择打开一个NH会话的动作)。如果要在操作完成后关闭OnActionExecuted会话,请关闭该会话。如果要在视图中打开会话,请关闭OnResultExecuted。

    当然,很多人不喜欢View中的Open Session,因为你无法控制纯粹在控制器中生成什么查询 - 视图触发了很多查询,这可能会导致不可预测的性能。

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