2011-08-11 179 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));