तो यहां स्थिति है: मान लीजिए कि मेरे पास लचीली खोज का प्रतिनिधित्व करने के लिए उपयोग की जाने वाली कक्षा संरचना है:

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

और इसी तरह, विभिन्न प्रकार के संग्रह के साथ।
ConcreteValues संग्रह को छोड़कर, तालिका-प्रति-विरासत का उपयोग करके शर्तों को मैप किया जाता है, जिन्हें अलग-अलग तालिकाओं (स्ट्रिंग मानों के लिए एक तालिका, इंट मानों के लिए एक तालिका आदि) में मैप किया जाता है।

मेरा प्रश्न है- मैं SearchDefinitions की सूची को कुशलतापूर्वक कैसे प्राप्त करूं? SearchTerms के संग्रह के लिए मैं 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!

इसे कैसे करें इस पर कोई सुझाव?
मैं यहां धाराप्रवाह मैपिंग जोड़ रहा हूं-

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
J. Ed 11 अगस्त 2011, 14:32

2 जवाब

सबसे बढ़िया उत्तर

बात यह है कि एक बार मैपिंग फ़ाइल में लाने की रणनीति को 'सबसेलेक्ट' के रूप में परिभाषित किया जाता है, एक प्रकार के संग्रह को प्रारंभ करने से यह उस प्रकार के संग्रह वाले सभी ऑब्जेक्ट्स पर प्रारंभ हो जाएगा।
अधिक जानकारी के लिए फॉलोविंग वर्किंग कोड देखें:

    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; 
0
J. Ed 8 सितंबर 2011, 18:26

JoinQueryOver या JoinAlias का उपयोग करते समय 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));
1
Firo 11 अगस्त 2011, 20:05