开发者

Eager loading multiple child collections within a paged result

开发者 https://www.devze.com 2023-02-18 13:53 出处:网络
I\'ve gone well beyond my NHibernate capabilities, it seems... I\'m trying to load Top10 of a given entity with three child collections, but NHibernate\'s Linq implementation gets me the correct resu

I've gone well beyond my NHibernate capabilities, it seems...

I'm trying to load Top10 of a given entity with three child collections, but NHibernate's Linq implementation gets me the correct results... at a cost of loading the entire table of all childs.

public class PartnerDto : BaseDto
{
    private IList<EmailContactDto> _emailContacts;
    private IList<TelephoneNumberDto> _telephoneNumbers;
    private IList<string> _chains;
    public EmailContactDto[] EmailContacts
    {
        get { return _emailContacts.ToArray(); }
        set { _emailContacts = value.ToList(); }
    }
    public TelephoneNumberDto[] TelephoneNumbers
    {
        get { return _telephoneNumbers.ToArray(); }
        set { _telephoneNumbers = value.ToList(); }
    }
    public string[] Chains
    {
        get { return _chains.ToArray(); }
        set { _chains = value.ToList(); }
    }
    //more properties
}

Mapping:

<class name="PartnerDto" table="[Partner]" schema-action="none" lazy="false" mutable="false">
<id name="Id">
  <generator class="hilo"/>
</id>
<bag name="Chains" table="PartnerChains" access="field.camelcase-underscore" lazy="false" fetch="subselect">开发者_运维知识库
  <key column="PartnerId"/>
  <element column="Chain" type="System.String"/>
</bag>
<bag name="TelephoneNumbers" access="field.camelcase-underscore" lazy="false" fetch="subselect">
  <key column="PartnerId"/>
  <composite-element class="TelephoneNumberDto">
    <property name="Name" not-null="true"/>
    <property name="ClickToDial" not-null="true"/>
    <property name="SMS" not-null="true"/>
    <property name="Index" column="[Index]" not-null="true"/>
  </composite-element>
</bag>
<bag name="EmailContacts" access="field.camelcase-underscore" lazy="false" fetch="subselect">
  <key column="PartnerId"/>
  <composite-element class="EmailContactDto">
    <property name="Name" not-null="true"/>
    <property name="Email" not-null="true"/>
    <property name="NewsLetter" not-null="true"/>
    <property name="Index" column="[Index]" not-null="true"/>
  </composite-element>
</bag>
<!-- other properties -->

When I do a:

session.Query<PartnerDto>().Take(10);

I get the correct 10 back, however my sql shows that it loads all rows in the TelephoneNumber table, EmailContact table and PartnerChains table instead of limiting it to the 10 Partners I'm loading.

What am I missing? (Yes, I have to load all eagerly - they are to be serialized right after the query.)

EDIT: Found a solution:

I first query for only the Id's of the paged query - then I query for the full object graph using these Id's. Since the Top 10 is now inside the where clause - it is used by NHibernate to filter the collections as well. Yes, it is not perfect - I have two roundtrips and five queries total, but still my best option at this point.


Use batch-size on your collections with the same count as your page size, and remove lazy= "false"

That will use a single query to get each collection type.

It's the easiest way to do it, and one of the most efficient.

Of course, the session must be kept open until you have serialized the objects.


I think the problem is not related to linqtoNH, but is limitation due to the fact that you are eagerly retrieving collection, this results in a single query with joins that can't be ranked ("top 10" ) because there is no more a one to one relation with record-entity in the raw resultset that come from the DB.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号