Linq到NHibernate - 渴望加载孙子孙而不是孩子

时间:2012-01-18 23:57:03

标签: c# nhibernate linq-to-nhibernate eager-loading grandchild

我正在使用NHibernate 3.2和FluentNHibernate以及Linq到NHibernate。我想使用Linq到NHibernate来急切加载一个集合的所有孙子,而不必加载孩子。例如,假设我有以下类:

public class Parent
{
    public virtual int Id { get; set; }
    public virtual IList<Child> Children { get; set; }
}

public class ParentMap : ClassMap<Parent>
{
    Id(x => x.Id);
    HasManyToMany(x => x.Children).ExtraLazyLoad();
}

public class Child
{
    public virtual int Id { get; set; }
    public virtual IList<Parent> Parents { get; set; }
    public virtual IList<Grandchild> Grandchildren { get; set; }
    public virtual ProhibitivelyLargeType ProhibitivelyLargeField { get; set; }
    public virtual ProhibitivelyLargeType RarelyUsedLargeField { get; set; }
}

public class ChildMap : ClassMap<Child>
{
    Id(x => x.Id);
    HasManyToMany(x => x.Parents).ExtraLazyLoad();
    HasManyToMany(x => x.Grandchildren).ExtraLazyLoad();
    Map(x => x.ProhibitivelyLargeField);
    Map(x => x.RarelyUsedField).LazyLoad();
}

public class Grandchild
{
    public virtual int Id { get; set; }
    public virtual IList<Child> Children { get; set; }
    public virtual int Age { get; set; }
}

public class GrandchildMap : ClassMap<Grandchild>
{
    Id(x => x.Id);
    HasManyToMany(x => x.Children).ExtraLazyLoad();
    Map(x => x.Age);
}

对于每位家长,我想知道所有父母的孙子孙女的总年龄。我可以使用以下方法:

Dictionary<Parent, int> grandchildAges = session.Query<Parent>()
    .FetchMany(p => p.Children)
    .ThenFetchMany(c => c.Grandchildren)
    .AsEnumerable()
    .ToDictionary(
        p => p,
        p => p.Children.SelectMany(c => c.Grandchildren).Sum(g => g.Age)
    );

此方法可生成正确的结果。但是,它需要加载所有Child对象。 Child包含一个类型为ProhibitivelyLargeType的字段,它不是延迟加载的,所以我真的不想加载有关Child但其ID的任何内容。但是,如果我不使用FetchMany / ThenFetchMany,那么我遇到了N + 1问题,并且每个Child和Grandchild都会访问数据库,这也是不可接受的。

或者,我可以制作ProhibitivelyLargeField LazyLoad。但是,大多数使用Child类的应用程序需要使用ProhibitivelyLargeField,但是他们不想加载RarelyUsedLargeField,它已经是LazyLoad。据我了解,加载一个LazyLoad属性会导致所有这些属性被加载,因此这个解决方案会使正常的用例陷入困境。

有没有办法使用Linq到NHibernate获取我正在寻找的信息,或者我是否必须使用Criteria Query API?

谢谢!

编辑了

以举例说明为什么使ProhibitivelyLargeField LazyLoad不合需要

2 个答案:

答案 0 :(得分:0)

以下是QueryOver。它只显示了将结果加载到两个较小步骤的想法。也许你可以把它翻译成LINQ

// inititialize the dictionary
Grandchild grandchild = null;
Dictionary<Parent, int> dict = session.QueryOver<Parent>()
    .JoinQueryOver(p => p.Childs)
    .JoinAlias(c => c.GrandChilds, () => grandchild)
    .Select(Projections.Group<Parent>(p => p.Id), Projections.Sum(() => grandchild.Age))
    .AsEnumerable()
    .Cast<object[]>()
    .ToDictionary(
        array => session.Load<Parent>(array[0]),
        array => (int)array[1]
    );

// initialize all Parent proxies
session.QueryOver<Patient>()
    .WhereProperty(p => p.Id).In(dict.Keys.Select(p => p.Id))
    .ToList();

答案 1 :(得分:-1)

我没有使用过nhibernate,但是我已经将linq用于实体,并且从我看到你做了大量的数据库查询。您应该执行单行查询,该查询仅返回所需的数据:

from parent in session.Parents
let children = parent.Children
select new {parent = parent, children.SelectMany(c => c.Grandchildren).Sum(gc => gc.Age)}

如果我出错了,请道歉。有一段时间没有做过C#,我正在打电话。

如果这种方法不起作用,别人告诉我,我会删除它。