2011-08-11 21 views
0

ので、ここでの状況です:検索用語が異なるフィールドを参照することができるので、用語の各タイプがあり、今フェッチ戦略

public class SearchDefinition 
{ 
    public virtual string Name {get; set;} 
    public virtual IEnumerable<SearchTerm> Terms {get; set;} 
} 

public abstract class SearchTerm 
{ 
    public virtual Operator Op {get; set; } //i.e 'In', 'Not in', 'Contains' etc.. 
    public abstract IEnumerable<object> CompareValues {get; } //the values against which the search is performed. for example- 'in (2,6,4)', 'contains ('foo', 'blah')'. 
} 

:私は柔軟な検索を表すために使用されるクラス構造を持っていると仮定独自のクラス:

public class NameSearchTerm : SearchTerm 
{ 
    public virtual IEnumberable<string> ConcreteValues {get; set;} 
    public override IEnumberable<object> CompareValues 
    { 
     get 
     { 
      return ConcreteValues.Cast<object>(); 
     } 
    } 
} 

など、さまざまな種類のコレクションがあります。
異なるテーブル(文字列値のテーブル、int値のテーブルなど)にマップされているConcreteValuesコレクションを除き、用語はテーブルごとの階層を使用してマップされます。

私の質問is-効率よくSearchDefinitionのリストを取得しますか? SearchTermのコレクションでは、私はselect戦略(選択N + 1になります)を使用することはできません。
しかし、正しいクエリを送信している間、JoinQueryOverまたはJoinAliasを使用して取り出し、コレクションを移入しません:

var definitions = session.QueryOver<SearchDefinition>() 
    .Where(/*condition*/) 
    .JoinAlias(d=> d.Terms,() => termsAlias) 
    .List(); //sends a correct, joined query which fetches also the terms from the terms table 

Assert.IsTrue(NHibernateUtil.IsInitialized(definitions[0].Terms)); //THIS FAILS! 

これを行う方法上の任意の提案を?
私は流暢なマッピングを追加しているhere-

SearchDefinitionクラス内の用語集:

mapping.HasMany(x => x.Terms) 
       //.Not.LazyLoad() 
       .Fetch.Subselect() 
       .Cascade.AllDeleteOrphan() 
       .Cache.ReadWrite(); 

(すべての用語クラスについても同様)IntSearchTermクラス内の具体的な値のコレクション:

mapping.HasMany<int>(t=> t.ConcreteValues).Table("TermsIntValues").Element("IntValue") 
       //.Not.LazyLoad() 
       .Fetch.Subselect() 
       .Cascade.AllDeleteOrphan(); 

答えて

0

事がマッピングファイル内フェッチ戦略は初期化し、「副選択」と定義されると、コレクションの一つのタイプは、コレクションのそのタイプが含まれているすべてのオブジェクトの上にそれを初期化するだろうということです。
詳細については、次の作業コードを参照してください。

var subQuery = QueryOver.Of<SearchDefinition>() 
     .Where(p => p.IsActive) 
     .Select(p => p.Id); 

    var searchDefinitionsQuery = Session.QueryOver<SearchDefinition>() 
     .WithSubquery.WhereProperty(p => p.Id).In(subQuery); 
     searchDefinitionsQuery.OrderBy(p => p.SortOrder).Asc(); 

    var searchDefinitionsWithTerms = searchDefinitionsQuery.Future(); 

    var intValuesQuery = Session.QueryOver<IntValuesTerm>() 
     .WithSubquery.WhereProperty(c => c.SearchDefinition.Id).In(subQuery) 
     .Future(); 

    var stringValuesQuery = Session.QueryOver<StringValuesTerm>() 
     .WithSubquery.WhereProperty(c => c.SearchDefinition.Id).In(subQuery) 
     .Future(); 

    var timespanValuesQuery = Session.QueryOver<TimeSpanValuesTerm>() 
     .WithSubquery.WhereProperty(c => c.SearchDefinition.Id).In(subQuery) 
     .Future(); 


    if (searchDefinitionsWithTerms.Count() == 0) 
    { 
     return searchDefinitionsWithTerms; 

    } 

    /*if the searchDefinitions collection isn't empty- make sure all collections are initialized. 
    * 
    * since our fetching strategies are all 'SubSelect' (see SearchDefinitionMappingOverride, SearchDefinitionTermsMappingOverride), 
    * all we need to do is inialize ONE collection of each type (intValuesTerms, string values Terms etc..), and then automatically all other collections of the same type will also be initialized. 
    * (look at the generated sql query for further info). 
    * for example: if we have 5 searchDefinitions, each with 1 Term of type 'IntValuesTerm', it's enough to initialize just one of those collections, and then all others of the same type will be initialized as well. 
    */ 


    //need to initalize each type of collection (int, string, timespan) once, in order for all the collections of that type to initialize 
    IntValuesTerm intTerm = (IntValuesTerm) searchDefinitionsWithTerms.SelectMany(p => p.Terms).FirstOrDefault(c => c is IntValuesTerm); 
    if (intTerm != null) 
    { 
     NHibernateUtil.Initialize(intTerm.IntValues); 
    } 

    StringValuesTerm stringTerm = (StringValuesTerm)searchDefinitionsWithTerms.SelectMany(p => p.Terms).FirstOrDefault(c => c is StringValuesTerm); 
    if (stringTerm != null) 
    { 
     NHibernateUtil.Initialize(stringTerm.StringValues); 
    } 

    TimeSpanValuesTerm timespanTerm = (TimeSpanValuesTerm)searchDefinitionsWithTerms.SelectMany(p => p.Terms).FirstOrDefault(c => c is TimeSpanValuesTerm); 
    if (timespanTerm != null) 
    { 
     NHibernateUtil.Initialize(timespanTerm.TimeSpanValues); 
    } 

    return searchDefinitionsWithTerms; 
1

JoinQueryOverまたはJoinAliasを使用している場合NHibernateは、条件を除外/除外できるため、コレクションを初期化しませんTermsコレクションに含まれるすべてのTermsを取得するわけではありません。私が考えることができる唯一の方法はサブクエリです。

var subquery = QueryOver.For<SearchDefinition>() 
    .Where(/*conditions*/) 
    .JoinAlias(d=> d.Terms,() => termsAlias) 
    .Where(/*Terms.conditions*/) 
    .Select(def => def.Id); 

var definitions = session.QueryOver<SearchDefinition>() 
    .WithSubquery.WhereProperty(def => def.Id).In(subquery); 
    .List(); 

Assert.IsTrue(NHibernateUtil.IsInitialized(definitions[0].Terms));