如何使用NHibernate加载大型复杂对象图

给定一个像这样的对象图:

A { IEnum } B { IEnum, IEnum, IEnum, ... } C { IEnum } 

如果没有N + 1问题,我怎么能急切地加载整个对象图?

这是我最终要执行的查询的伪代码:

 var a = Session.Get(1); // Query 1 var b_Ids = foreach(b in A.B's) => Select(b.Id); // Query 2 var c = Session.CreateQuery("from C where B in (b_Ids)").Future(); // Query 3 var d = Session.CreateQuery("from D where B in (b_Ids)").Future(); // Query 3 var e = Session.CreateQuery("from E where B in (b_Ids)").Future(); // Query 3 // Iterate through c, d, e, ... find the correct 'B' parent, add to collection manually 

我使用这种方法的问题是,当我将’C’,’D’和’E’的实例添加到父’B’的相应集合时,该集合仍然被代理,何时。调用Add(),代理初始化自己并执行更多查询; 我认为NHibernate无法看到我已经拥有一级缓存中的所有数据,这是可以理解的。

我尝试通过在Add方法中执行以下操作来解决此问题:

 void Add(IEnum) { _collection = new Collection(); // replace the proxied instance to prevent initialization foreach(c) => _collection.Add(c); } 

这给了我一个我想要的最佳查询策略,但是后来在做持久性时赶上了我(NHibernate跟踪原始集合的-ref,从我能说的东西)。

所以我的问题是,如何在没有N + 1的情况下为孩子的孩子加载复杂的图表? 我到目前为止唯一遇到的是加入BC,BD,BE这在我的情况下是不可接受的。

我们使用NH 2.1.2和FluentHN进行映射。 升级到NH的v3或使用hbm的/存储过程/任何不在桌面上的东西。

更新:其中一条评论引用了一种加入方法,我确实遇到了一个演示这种方法的博客。 这种解决方案在我们的情况下是不可接受的,但它可以帮助其他人: 渴望在NHibernate的一次往返中获取多个子集合

更新2:乔丹的回答让我回到了与我的问题相关的以下post: 类似问题和艾恩德的博客 。 此时悬而未决的问题是“如何在没有每路径往返的情况下执行子选择”。

更新3:即使子选择解决方案不是最优的,我也接受了乔丹的回答。

您可以使用可在映射文件中设置的SubSelect提取。 这将避免N + 1和笛卡尔积。

首先,您可以更改映射以热切地加载这些集合。 请参阅本节中的第4项。
其次 – 我相信你的集合似乎加载两次的原因是你首先使用查询获取它,然后使用collection属性。
意思是 – nHibernate区分用户生成的查询(如您使用的查询)和自己生成的查询(就像您第一次阅读’C’集合时出现的查询)。 他们不混。
因此,当您第一次阅读“C”集合时,nHib无法识别它实际上曾向DB发送完全相同的查询(因为它是用户查询),并再次发送它。
避免这种情况的方法是通过B实体检索您的C集合。