2012-01-18 12 views
3

NHibernateにNHibernateとLinqを使ってNHibernate 3.2を使用しています。私はLinqをNHibernateに使って、子をロードせずにコレクションのすべての孫を熱心にロードしたいと思っています。例えば、私は次のクラスを持っていることを言う: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という型のフィールドを含んでいますが、これは遅延ロードされていないので、私は本当に子については何もロードしないことを好みます。しかし、FetchMany/ThenFetchManyを使用しないと、N + 1の問題が発生し、それぞれの子どもと孫のデータベースへの移動が行われますが、これも受け入れられません。

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

NHibernateにLinqを使用して探している情報を取得する方法があるのですか、またはCriteria Query APIを使用する必要がありますか?

ありがとうございます!

はProhibitivelyLargeField LazyLoadは望ましくない

+0

は、lazyloadedオプションとして 'ProhibitivelyLargeType'を定義していますか? – Firo

+0

@Firoいいえ、ProhibitivelyLargeFieldにlazyloadedすることはできません。 – chris4600

+0

... ...?遅延ロードを無効にする有効な例をまだ1つは見ていません。 –

答えて

0

次はQueryOverあるかもしれない作る理由の例を与えるために編集しました。 2つの小さなステップで結果をロードするというアイデアを示すことだけです。たぶん、あなたは、私はNHibernateはを使用していないが、私はエンティティにLINQを使用している、と私は見たものから、データベースクエリのトンをしている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(); 
+0

ありがとうございます。あなたが提供したコードは、私が今やっていることに似ています。 「LINQに翻訳する」の部分は、私が困惑させることができなかった部分です。 – chris4600

-1

にそれを翻訳することができます。代わりに、必要なデータのみを返す1行のクエリを実行する必要があります。

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

私は何か問題が生じたら謝罪します。しばらくの間C#をやっていないし、私は自分の電話にいます。

この方法では誰かが私に言い聞かせるとうまくいかない場合は削除します。

+0

ありがとう、私はLinqがNHibernateを正しく理解していれば、あなたが提供したような直接的な実装は、各子供と各孫のデータベースへの1回の旅行をするでしょう。 (FetchMany/ThenFetchMany部分は、1回ですべてを取得することです) – chris4600

+0

これは奇妙なことですが、テストしましたか? – tster