在 NHibernate 3.0 Linq 中急切加载多个兄弟姐妹和孙子(表兄弟姐妹?)的良好行为
我正在尝试使用 NHibernate 3.0 的 LINQ 接口执行以下操作。我想查询一个对象(使用一些Where子句),并加载一些子对象和孙对象。目前我正在这样做:
var results = session.Query<Thing>()
.Where(...)
.Fetch(x => x.SubThingA)
.ThenFetch(st => st.SubSubThingA)
.Fetch(x => x.SubThingB)
.ThenFetch(st => st.SubSubThingB)
// etc...
但是,这会导致所有孙子之间的笛卡尔积(每个结果行包含很多很多列)。这是由“ayende”
我怎样才能以更好的方式(SQL 和性能方面)做到这一点,仍然使用 NHibernate 的 LINQ 接口?
(一方面,我注意到当前使用 Fetch 时 ToFuture 方法不起作用)
非常感谢!
I'm trying to do the following with NHibernate 3.0's LINQ interface. I want to query for an object (using some Where clause), and load some children and grandchildren. Currently I'm doing it like so:
var results = session.Query<Thing>()
.Where(...)
.Fetch(x => x.SubThingA)
.ThenFetch(st => st.SubSubThingA)
.Fetch(x => x.SubThingB)
.ThenFetch(st => st.SubSubThingB)
// etc...
However, this results in a Cartesian product between all grandchildren (every result row contains many, many columns). This is discussed by "ayende" here. On the other hand I get a single round-trip, unlike splitting the query and then combining it.
How can I do it in a better (SQL and performance-wise) way, still using NHibernate's LINQ interface?
(For one thing, I've noticed that currently the ToFuture methods don't work when you use Fetch)
Thanks a lot!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在大多数情况下,通过在实体和集合中使用
batch-size
(而不是创建大型查询)可以获得更好的性能。最好的情况是根据每个根实体类型的 id 进行查询。
假设您有一个根实体 Customer,其中包含 Orders 集合,其中包含 OrderItems 集合,其中引用 Products ,并且所有
batch-size
属性均设置为1000。假设您检索包含 10 个客户的列表,其中平均有 10 个订单,每个订单有 10 个产品:
因此,您只需 4 个查询,完全没有连接,通过 PK 检索所有内容。这既简单又高效。
In most cases, you'll get better performance by using
batch-size
in entities and collections instead of creating a mega-query.Best case scenario, it's a query by id per root entity type.
Let's say you have a root entity Customer, which has a collection of Orders, which have a collection of OrderItems, which reference Products, and all
batch-size
properties are set to 1000.Say you retrieve a list of 10 customers, which have in average 10 orders with 10 products each:
So, you have just 4 queries, with no joins at all, retrieving everything by PK. It's easy and efficient.
尽管 Diego 的答案是在 NHibernate 中执行这些操作的公认方法,但我真的对这种方法感到不舒服。我不想仅仅因为我可能需要以某些方式检索它们就必须为我的对象定义显式契约。另外,我并不总是想将它们连载。此外,在许多情况下,我知道最好的性能始终是获取所有数据的单次往返。
我最终使用的解决方案是实现一个函数,该函数采用根对象上的(类型安全)表达式列表,例如
其中 SubInclude 是 IEnumerable 的扩展方法,在解析这些表达式时使用。
我解析这个表达式列表,并为每个表达式(x、x.Child、x.Child.GrandChild1)的每个子路径构建一个根类型上的 NHibernate 标准查询:
这对于列表中的每个表达式都重复。
最后一行确保此急切的获取将包含在接下来发生的任何往返中。然后,我对根对象 T 进行实际查询,并且会话在同一往返中自动执行获取我在表达式中传递的每个路径所需的所有查询。
对每个表达式路径单独执行查询,因此不存在笛卡尔积问题。
底线是,这不是一件简单的事情。对于我来说,代码有点太多,无法按原样发布。我更喜欢 EF4.1 的 Include(表达式) API,它会自动完成所有这些操作。
Although Diego's answer is the accepted method for doing these things in NHibernate, I really don't feel comfortable with that approach. I don't want to have to define explicit contracts for my objects just because I may need to retrieve them in certain ways. Also, I don't always want to serialize them at all. Furthermore, in many cases I know that the best performance will always be a single round-trip to fetch all the data.
The solution I ended up using was to implement a function that takes a list of (type safe) expressions on the root object, such as
Where SubInclude is an extension method for IEnumerable that is used when parsing these expressions.
I parse this list of expressions and build, for each subpath of each expression (x, x.Child, x.Child.GrandChild1) an NHibernate criteria query on the root type:
This is repeated for every expression in the list.
The last line makes sure that this eager fetch will be included in whatever round-trip happens next. Then I make the actual query on the root object T, and the session automatically performs in that same round-trip all the queries required to fetch each of the paths I've passed in the expressions.
The queries are performed separately for each expression path, so there is no cartesian-product problem.
Bottom line is that this is no simple feat. There's a little too much code for me to publish as-is. I prefer EF4.1's Include(expression) API, which does all of this automagically.