3

私はNHibernate3.2とFluentNHibernateおよびLinqtoNHibernateを使用しています。Linq to 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にはProhibitivelyLargeTypeタイプのフィールドが含まれていますが、これは遅延ロードされないため、ChildについてはID以外は何もロードしないほうがよいでしょう。ただし、FetchMany / ThenFetchManyを使用しない場合は、N + 1の問題が発生し、子と孫ごとにデータベースにアクセスする必要があります。これも受け入れられません。

または、ProhibitivelyLargeFieldLazyLoadを作成することもできます。ただし、Childクラスを使用するほとんどのアプリケーションは、ProhibitivelyLargeFieldを使用する必要がありますが、すでにLazyLoadであるRarelyUsedLargeFieldをロードする必要はありません。私が理解しているように、1つのLazyLoadプロパティをロードすると、すべてのプロパティがロードされるため、このソリューションは通常のユースケースを台無しにします。

Linq to NHibernateを使用して探している情報だけを取得する方法はありますか、それともCriteria Query APIを使用する必要がありますか?

ありがとう!

ProhibitivelyLargeFieldLazyLoadを作成することが望ましくない理由の例を示すために編集

4

2 に答える 2

0

以下はQueryOverです。2 つの小さなステップで結果をロードするという考えを示すだけです。多分あなたはそれを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();
于 2012-01-20T16:24:54.110 に答える
-1

私はnhibernateを使用していませんが、エンティティにlinqを使用しました。これから、大量のデータベースクエリを実行していることがわかります。代わりに、必要なデータのみを返す1行のクエリを実行する必要があります。

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

何かおかしなことがあったらお詫びします。しばらくC#を実行しておらず、電話をかけています。

このアプローチがうまくいかない場合は、誰かに教えてください。削除します。

于 2012-01-20T17:09:15.537 に答える