Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/76.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 在NHibernate 3.0 Linq中,多个兄弟姐妹和孙子女(表亲?)的良好行为_Sql_Performance_Nhibernate_Linq To Nhibernate_Eager Loading - Fatal编程技术网

Sql 在NHibernate 3.0 Linq中,多个兄弟姐妹和孙子女(表亲?)的良好行为

Sql 在NHibernate 3.0 Linq中,多个兄弟姐妹和孙子女(表亲?)的良好行为,sql,performance,nhibernate,linq-to-nhibernate,eager-loading,Sql,Performance,Nhibernate,Linq To Nhibernate,Eager Loading,我正在尝试使用NHibernate 3.0的LINQ接口执行以下操作。我想查询一个对象(使用somewhere子句),并加载一些子对象和子对象。目前我是这样做的: var results = session.Query<Thing>() .Where(...) .Fetch(x => x.SubThingA) .ThenFetch(st => st

我正在尝试使用NHibernate 3.0的LINQ接口执行以下操作。我想查询一个对象(使用somewhere子句),并加载一些子对象和子对象。目前我是这样做的:

var results = session.Query<Thing>()
                     .Where(...)
                     .Fetch(x => x.SubThingA)
                     .ThenFetch(st => st.SubSubThingA)

                     .Fetch(x => x.SubThingB)
                     .ThenFetch(st => st.SubSubThingB)

                     // etc...
var results=session.Query()
.其中(…)
.Fetch(x=>x.SubThingA)
.ThenFetch(st=>st.subthinga)
.Fetch(x=>x.SubThingB)
.ThenFetch(st=>st.subthingB)
//等等。。。
但是,这将导致所有子代之间的笛卡尔乘积(每个结果行包含许多列)。“ayende”对此进行了讨论。另一方面,我只得到一次往返,这与拆分查询然后组合查询不同

我怎样才能以更好的方式(SQL和性能方面)实现它,仍然使用NHibernate的LINQ接口

(首先,我注意到当前使用Fetch时ToFuture方法不起作用)


非常感谢

在大多数情况下,通过在实体和集合中使用
批量大小
而不是创建大型查询,可以获得更好的性能

最好的情况是,它是按每个根实体类型的id进行的查询


假设您有一个根实体客户,它有一个订单的集合,它有一个订单项的集合,引用产品,并且所有
批量
属性都设置为1000

假设您检索到一个包含10个客户的列表,这些客户平均有10个订单,每个订单包含10种产品:

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

因此,您只有4个查询,根本没有连接,通过PK检索所有内容。这很简单,也很有效。

虽然Diego的答案是在NHibernate中做这些事情的公认方法,但我真的对这种方法感到不舒服。我不想仅仅因为需要以某些方式检索对象而必须为对象定义显式契约。另外,我并不总是想序列化它们。此外,在许多情况下,我知道最佳性能始终是获取所有数据的一次往返

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

x => x.Child.GrandChild1
x => x.Child.GrandChild2Collection.SubInclude(c => c.GreatGrandChild)
其中SubInclude是在解析这些表达式时使用的IEnumerable的扩展方法

我解析表达式列表,并为每个表达式(x,x.Child,x.Child.1)的每个子路径构建根类型上的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()
var queryOver=session.queryOver()。其中(…选择根对象的表达式…);
对于当前表达式中的每个子路径:
queryOver.RootCriteria.SetFetchMode(子路径,FetchMode.Eager)
queryOver.RootCriteria
.SetResultTransformer(新DistincTrotentyResultTransformer())
queryOver.Future()
对列表中的每个表达式重复此操作。 最后一行确保这个急切的获取将包含在接下来发生的任何往返中。然后我对根对象T进行实际查询,会话将在同一个往返过程中自动执行获取我在表达式中传递的每个路径所需的所有查询

对每个表达式路径分别执行查询,因此不存在笛卡尔积问题


底线是这不是一个简单的壮举。代码太多了,我无法按原样发布。我更喜欢EF4.1的Include(expression)API,它可以自动完成所有这一切。

一个相关的问题:有没有一种方法可以使用LINQ在一次往返中执行多个查询?(考虑到Future在使用Fetch的LINQ查询上似乎不起作用)我们在这里谈论的孩子有多少?将一个完整的对象图发送到远程客户端(我也在这样做)比将数据库命中两到三次(而不是一次)成本更高。在这种情况下,我通常拆分查询。此外,你应该考虑如果你需要从一开始就需要完整的对象图,或者如果你还可以在需要时动态加载一些其他的孩子。Florian Lim,这个对象有很多,很多孩子。我也更愿意点击数据库而不是客户端,但我正试图避免这一点,并在一个查询中执行整个过程。好吧,NHibernate Linq 3.0无法完成这一点,但我会关注这个问题,如果你得到一个有效的答案,我会很乐意使用它。(+1)谢谢你的答复。我的情况是,我需要加载所有需要的对象图(它被发送到远程客户端)。从我收集的信息来看,当您维护一个开放会话并使用惰性接口时,批处理大小是好的。当从一开始就需要一个大图时,最好的解决方案是什么?@sinelaw:从你的评论中,我知道你试图“按原样”序列化一个对象图。不要。你只是在人为地限制自己。迭戈——你说的“人为地限制自己”是什么意思?你能解释一下你指的是什么问题吗?@sinelaw:如果你使用显式契约序列化了图形,你就可以让NH发挥它的魔力。Diego-好的,明白了,听起来确实是个更好的主意。重要提示:我的实体类中的所有集合都定义为集合。否则,NHibernate最终会返回孙辈的副本和更深的点头