2011-08-11 177 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>(); 
     } 
    } 
} 

等,與不同類型的集合。
術語使用table-per-heirarchy進行映射,除了ConcreteValues集合之外,它們映射到不同的表(字符串值表,int值表等)。

我的問題是 - 如何有效檢索SearchDefinition的列表?對於收集SearchTerm s我不能使用select策略(將導致選擇N + 1)。
然而,獲取使用JoinQueryOverJoinAlias,在發送正確的查詢,不填充集合:

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! 

如何做到這一點有什麼建議?
我添加了流暢的映射這裏 -

條款集合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

當使用JoinQueryOverJoinAlias NHibernate不會初始化該集合,因爲您可以/可以過濾出條款,因此可能會有並非所有在「條款」集合中提取的「條款」。我能想到的唯一方法是子查詢。

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));