问题描述
给出一个像这样的对象图:
Given an object graph like:
A { IEnum<B> }
B { IEnum<C>, IEnum<D>, IEnum<E>, ... }
C { IEnum<X> }
如何急切地加载整个对象图而没有N + 1个问题?
How can I eagerly load the entire object graph without N+1 issues?
这是我最终想要执行的查询的伪代码:
Here is the pseudo code for the queries that I would ultimately like to execute:
var a = Session.Get<A>(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<C>(); // Query 3
var d = Session.CreateQuery("from D where B in (b_Ids)").Future<D>(); // Query 3
var e = Session.CreateQuery("from E where B in (b_Ids)").Future<E>(); // Query 3
// Iterate through c, d, e, ... find the correct 'B' parent, add to collection manually
这种方法的问题在于,当我将"C","D"和"E"的实例添加到父级"B"的相应集合中时,该集合仍被代理,当调用.Add()时,代理会初始化自身并执行更多查询;我认为NHibernate无法看到我已经将所有数据存储在一级缓存中了,这是可以理解的.
The problem that I have with this approach is that when I go to add the instances of 'C', 'D', and 'E' to the corresponding collection of the parent 'B', the collection is still proxied, and when .Add() is called, the proxy initializes itself and executes more queries; I think NHibernate is not capable of seeing that I already have all of the data in first level cache, which is understandable.
我尝试通过在Add方法中执行以下操作来解决此问题:
I've tried to work around this problem by doing something like this in my Add method:
void Add(IEnum<C>)
{
_collection = new Collection<C>(); // replace the proxied instance to prevent initialization
foreach(c) => _collection.Add(c);
}
这为我提供了我想要的最佳查询策略,但后来在进行持久性处理时就跟上了我的步(NHibernate从我所知道的地方跟踪原始集合的by-ref).
This gave me the optimum query strategy that I wanted, but caught up with me later when doing persistence (NHibernate tracks the original collection by-ref somewhere from what I can tell).
所以我的问题是,如何加载带有N + 1个孩子的孩子的复杂图?到目前为止,我遇到的唯一一件事就是加入B-C,B-D,B-E,这在我的情况下是不可接受的.
So my question is, how can I load a complex graph with children of children without N+1? The only thing I've come across to date is joining B-C, B-D, B-E which is not acceptable in my situation.
我们使用带有FluentHN的NH 2.1.2进行映射.升级到NH v3或使用hbm/存储的procs/进行任何升级都不会失败.
We are using NH 2.1.2 with FluentHN for mapping. An upgrade to v3 of NH or using hbm's/stored procs/whatever would not be off the table.
更新:其中的一条评论提到了连接方法,而我确实遇到了一个展示这种方法的博客.在我们的情况下,这种解决方法是不可接受的,但它可能会对其他人有所帮助:与NHibernate进行一次往返时,渴望获取多个子集合
UPDATE:One of the comments references a join approach, and I did come across a blog that demonstrates this approach. This work around is not acceptable in our situation, but it may help someone else: Eager fetch multiple child collections in 1 round trip with NHibernate
更新2:Jordan的回答使我找到了与我的问题有关的以下帖子:和 Ayende的博客.此时悬而未决的问题是如何在没有每个路径往返的情况下执行子选择".
UPDATE 2:Jordan's answer led me to the following posts that are related to my question: Similar Question and Ayende's blog. The pending question at this point is "how can you perform the subselects without a round trip per-path".
更新3:即使子选择解决方案不是最佳选择,我也接受了约旦的回答.
UPDATE 3:I've accepted Jordan's answer even though the subselect solution is not optimal.
推荐答案
您可以使用SubSelect提取,可以在映射文件中对其进行设置.这样可以避免N + 1和笛卡尔积.
You can use SubSelect fetching which can be setup in the mapping files. This will avoid N+1 and cartesian product.
这篇关于如何使用NHibernate加载大型,复杂的对象图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!