我在初始化[type]的第一个(流畅的)nhibernate查询中出错-失败。。。没有会话或会话已关闭

我在初始化[type]的第一个(流畅的)nhibernate查询中出错-失败。。。没有会话或会话已关闭,nhibernate,fluent,Nhibernate,Fluent,我刚从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"); Ha

我刚从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");
    }
}
然后,我尝试使用以下方法选择我添加的客户:

        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.CustomerD2F1C5-7d9e-4f77-8b4f-9e200088187b]失败 要延迟初始化角色集合,请执行以下操作: fnh.DataModel.Kunde.\u联系人,未关闭任何会话或会话


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

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

查看时保持会话打开 这就是所谓的开放过程吗 会话视图方法。您可能正在关闭控制器中的NH会话,对吗

快速加载整个对象图 在服务器上使用.Not.Lazyload 联络。不推荐

将所需的所有数据复制到视图中 控制器中的模型,并传递 这是我的看法。使用automapper来帮助您完成此操作

回应评论的最新情况:

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

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

在global.asax-open In Application_BeginRequest和close In Application_EndRequest中,请参见:为了简单起见,如果您想在视图中执行open Session,我建议从这个开始

在http模块中,与上一个模块基本相同,但模块化请参见

对于最后两个问题,您可能会想,‘但这将意味着为每个页面请求创建一个会话!’你是对的,但实际上有多少页面不会进入数据库?此外,一旦创建了会话工厂,会话创建就是轻量级的

使用一个属性来修饰action方法,基本上可以执行与前两个相同的操作,但是通过这种方式,您可以选择哪些操作打开NH会话。如果希望在操作完成后关闭OnActionExecuted中的会话,请关闭该会话。如果要在视图中打开会话,请关闭OnResultExecuted。 当然,许多人不喜欢视图中的Open Session,因为您无法完全控制在控制器中生成的查询-视图触发了许多查询,这可能会导致不可预测的性能


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

非常感谢您给出了这个伟大的答案。目前我确实使用了2,因为我的联系人表不太大。但是你能解释一下在这种情况下懒散的感觉在哪里,或者我只是为了理解而在错误的上下文中使用了它吗?因为我似乎必须将会话传递到UI并在那里关闭它,对吗?因此,我的UI将直接依赖于DAL。还是有一种模式可以实现懒散加载?3使用Not.LazyLoading时,该孤子与a 2相同。对吗?没问题!请参阅更新的答案。在我看来,这件事比应该的要复杂得多,但一旦你选择了你的方法并坚持下去,就不会太糟糕了。你应该同意1或3我不推荐2。此外,一旦您使用了此选项,您应该研究如何将事务用于所有查询,甚至是读取-原因如下:
        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();
            }
        }
    }