在 NHibernate 3.0 Linq 中急切加载多个兄弟姐妹和孙子(表兄弟姐妹?)的良好行为

发布于 2024-10-19 17:19:04 字数 617 浏览 7 评论 0原文

我正在尝试使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

柳若烟 2024-10-26 17:19:04

在大多数情况下,通过在实体和集合中使用batch-size(而不是创建大型查询)可以获得更好的性能。

最好的情况是根据每个根实体类型的 id 进行查询。


假设您有一个根实体 Customer,其中包含 Orders 集合,其中包含 OrderItems 集合,其中引用 Products ,并且所有batch-size 属性均设置为1000

假设您检索包含 10 个客户的列表,其中平均有 10 个订单,每个订单有 10 个产品:

var results = session.Query<Customer>().Where(...).Take(10).ToList();
  • 第一个查询将仅获取客户。
  • 当您开始迭代第一个 customer.Orders 集合时,将使用一个查询来加载所有这些集合(对于所有客户
  • 当您开始迭代第一个 order.OrderItems 集合时,将使用一个查询加载所有它们(对于所有订单和所有客户
  • 当您从第一个产品读取属性时,将使用一个查询来加载所有它们

因此,您只需 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:

var results = session.Query<Customer>().Where(...).Take(10).ToList();
  • The first query will fetch just the customers.
  • When you start iterating the first customer.Orders collection, one query will be used to load all of them (for all the customers)
  • When you start iterating the first order.OrderItems collection, one query will be used to load all of them (for all the orders and all the customers)
  • When you read a property from the first product, one query will be used to load all of them

So, you have just 4 queries, with no joins at all, retrieving everything by PK. It's easy and efficient.

櫻之舞 2024-10-26 17:19:04

尽管 Diego 的答案是在 NHibernate 中执行这些操作的公认方法,但我真的对这种方法感到不舒服。我不想仅仅因为我可能需要以某些方式检索它们就必须为我的对象定义显式契约。另外,我并不总是想将它们连载。此外,在许多情况下,我知道最好的性能始终是获取所有数据的单次往返。

我最终使用的解决方案是实现一个函数,该函数采用根对象上的(类型安全)表达式列表,例如

x => x.Child.GrandChild1
x => x.Child.GrandChild2Collection.SubInclude(c => c.GreatGrandChild)

其中 SubInclude 是 IEnumerable 的扩展方法,在解析这些表达式时使用。

我解析这个表达式列表,并为每个表达式(x、x.Child、x.Child.GrandChild1)的每个子路径构建一个根类型上的 NHibernate 标准查询:

var queryOver = session.QueryOver<T>().Where( ...expression to select root objects... );
for every subpath in the current expression:
    queryOver.RootCriteria.SetFetchMode(subPath, FetchMode.Eager)

queryOver.RootCriteria
         .SetResultTransformer(new DistinctRootEntityResultTransformer())

queryOver.Future()

这对于列表中的每个表达式都重复。
最后一行确保此急切的获取将包含在接下来发生的任何往返中。然后,我对根对象 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

x => x.Child.GrandChild1
x => x.Child.GrandChild2Collection.SubInclude(c => c.GreatGrandChild)

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:

var queryOver = session.QueryOver<T>().Where( ...expression to select root objects... );
for every subpath in the current expression:
    queryOver.RootCriteria.SetFetchMode(subPath, FetchMode.Eager)

queryOver.RootCriteria
         .SetResultTransformer(new DistinctRootEntityResultTransformer())

queryOver.Future()

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文