con*_*tor 7 c# fetching-strategy linq-to-nhibernate
我有这个对象图:
// Lots of stuff omitted for brevity; these are all virtual properties and there
// are other properties which aren't shown on all classes.
class A {
B b;
C c;
DateTime timestamp;
}
class B {
X x;
Y y;
}
class X {
int id;
}
class C { }
class Y { }
or to put it more simply,
a = {
b: {
x { id: int },
y: { }
},
c: { },
timestamp: DateTime
}
Run Code Online (Sandbox Code Playgroud)
现在我正在进行一个查询,我将返回一个As 列表,我需要他们所有的Bs,Cs,Xs和Ys.我也打算用B将它们分组到查找中.
ILookup<B, A> GetData(List<int> ids) {
using (ISession session = OpenSession()) {
var query = from a in session.Query<A>()
where ids.Contains(a.b.x.id)
orderby A.timestamp descending
select a;
query = query
.Fetch(a => a.b)
.ThenFetch(b => b.x)
.Fetch(a => a.b)
.ThenFetch(b => b.y)
.Fetch(a => a.c);
return query.ToLookup(a => a.b);
}
}
Run Code Online (Sandbox Code Playgroud)
有几点需要注意:
ToLookup因为group by当你需要所有实际值时,使用似乎更复杂 - 你需要查询数据库中的组,然后查询它们的实际值.我的问题是如何正确指定提取策略.我完成它的方式是我发现它运行的唯一方法(已经获取了所有的bx和值) - 但它产生了似乎错误的SQL:
select /* snipped - every mapped field from a0, b1, x2, b3, y4, c5 - but not b6 */
from [A] a0
left outer join [B] b1
on a0.B_id = b1.BId
left outer join [X] x2
on b1.X_id = x2.XId
left outer join [B] b3
on a0.B_id = b3.BId
left outer join [Y] y4
on b3.Y_id = y4.YId
left outer join [C] c5
on a0.C_id = c5.CId,
[B] b6
where a0.B_id = b6.BId
and (b6.X_id in (1, 2, 3, 4, 5))
order by a0.timestamp desc
Run Code Online (Sandbox Code Playgroud)
正如你所看到的那样,它获得了a.b3次的值- b1以及b3获取和b6where子句.
.Fetch电话,所以它只提取a.b一次?如果在一个查询中对一对多属性进行多次提取,则会得到笛卡尔积。NHibernate不能处理这个问题-AFAIK,是故意使它表现得像真正的SQL连接一样。HQL做同样的事情。
您无需一口气完成所有提取操作。拆分查询并在单独的查询中执行每个一对多获取/连接。每个对象都将在会话中缓存其数据,并正确连接所有对象引用。(注意:我从未在LINQ上尝试过此操作,但它确实在HQL中有效,原理相同)
在我的头顶上,它可能看起来像这样:
ILookup<B, A> GetData(List<int> ids) {
using (ISession session = OpenSession()) {
var query = from a in session.Query<A>()
where ids.Contains(a.b.x.id)
orderby A.timestamp descending
select a;
query
.Fetch(a => a.b)
.ThenFetch(b => b.x)
.ToList();
query
.Fetch(a => a.b)
.ThenFetch(b => b.y)
.Fetch(a => a.c)
.ToList();
return query.ToLookup(a => a.b);
}
Run Code Online (Sandbox Code Playgroud)
您可以做进一步的优化,使用ToFuture()方法而不是ToList()...我不确定它如何与LINQ和ToLookup方法一起使用,但是要正确一点也不应该太难。ToFuture()会将查询排队并作为一个sql命令执行它们,而不是为每个查询单独进行数据库连接。