Linq to NHibernate ThenFetch多个属性

时间:2010-12-31 23:00:31

标签: c# linq-to-nhibernate fetching-strategy

我有这个对象图:

// 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
}      

现在我正在进行查询,我将返回A的列表,我需要所有B个,C s,X s和Y s。我也打算用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);
   }
}

有几点需要注意:

  1. 这是一份需要返回所有数据的报告 - 无限结果不是问题。
  2. 我正在使用ToLookup进行分组,因为当您需要所有实际值时,使用group by似乎更复杂 - 您需要在数据库中查询组,然后查询他们的实际价值。
  3. 我的问题是如何正确指定提取策略。我完成它的方式是我发现它运行的唯一方法(已经获取了所有的b.x和b.y值) - 但是它产生了似乎错误的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
    

    正如您所看到的那样,获取a.b 3次的值 - b1b3用于获取,b6用于where子句。

    1. 我认为这会对数据库性能产生负面影响 - 我是否正确?
    2. 有没有办法修改我的.Fetch来电,因此它只会提取a.b一次?
    3. 这是解决我问题的好方法吗?

1 个答案:

答案 0 :(得分:4)

如果您在一个查询中多次提取一对多属性,则会获得一个笛卡尔积。 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);
}

你可以做一个进一步的优化,使用ToFuture()方法而不是ToList()...我不确定它如何与LINQ和ToLookup方法一起工作,但它不应该太难以正确。 ToFuture()将对查询进行排队并立即执行所有查询,而不是为每个查询执行单独的数据库连接。