1

私はこのような実体と流暢なマッピングを持っています。

public class Client : EntityWithTypedId<long>
{               
    [Length(Max=50)]
    public virtual string GivenName { get; set; }

    public virtual IList<Address> Addresses { get; set; }
}

public class ClientMap : ClassMap<Client> 
{       
    public ClientMap() 
    {
        Schema("dbo");
        Table("Client");            
        Id(x => x.Id, "ClientId").GeneratedBy.Identity();           
        Map(x => x.GivenName, "GivenName");             
        HasManyToMany(x => x.Addresses)
            .FetchType.Join()
            .Cascade.AllDeleteOrphan()
            .Table("ClientAddress")
            .ParentKeyColumn("ClientId")
            .ChildKeyColumn("AddressId")
            .AsBag();
    }           
}

次に、このようなICriteriaクエリを実行します

return Session.CreateCriteria<Client>()
    .CreateAlias("Organisation", "o").SetFetchMode("o", FetchMode.Join)
    .CreateAlias("Addresses", "a").SetFetchMode("a", FetchMode.Join)
    .Add(expression)
    .AddOrder(Order.Asc("Surname")).AddOrder(Order.Asc("GivenName"))
    .SetResultTransformer(new DistinctRootEntityResultTransformer())
    .SetMaxResults(pageSize)
    .SetFirstResult(Pagination.FirstResult(pageIndex, pageSize))
    .Future<Client>();

NHProfを使用すると、このようなクエリが実行され、すべてのクライアントの詳細とアドレスが返されることがわかります。

SELECT   top 20 this_.ClientId       as ClientId5_2_,
                this_.GivenName      as GivenName5_2_,
                addresses4_.ClientId as ClientId,
                a2_.AddressId        as AddressId,
                a2_.AddressId        as AddressId0_0_,
                a2_.Street           as Street0_0_,
                a2_.Suburb           as Suburb0_0_,
                a2_.State            as State0_0_,
                a2_.Postcode         as Postcode0_0_,
                a2_.Country          as Country0_0_,
                a2_.AddressTypeId    as AddressT7_0_0_,
                a2_.OrganisationId   as Organisa8_0_0_,
                o1_.OrganisationId   as Organisa1_11_1_,
                o1_.Description      as Descript2_11_1_,
                o1_.Code             as Code11_1_,
                o1_.TimeZone         as TimeZone11_1_
FROM     dbo.Client this_
         inner join ClientAddress addresses4_
           on this_.ClientId = addresses4_.ClientId
         inner join dbo.Address a2_
           on addresses4_.AddressId = a2_.AddressId
         inner join dbo.Organisation o1_
           on this_.OrganisationId = o1_.OrganisationId
WHERE    (o1_.Code = 'Demo' /* @p4 */
          and (this_.Surname like '%' /* @p5 */
                or (this_.HomePhone = '%' /* @p6 */
                     or this_.MobilePhone = '%' /* @p7 */)))
ORDER BY this_.Surname asc,
         this_.GivenName asc

期待どおりにすべてのレコードを返します

しかし、私がその後のようなコードを書く場合

foreach(var client in clients)
{
   if (client.Addresses.Any())
   { 
       Console.WriteLn(client.Addresses.First().Street);
   }
}

それでも、各アドレスで選択を行うN+1の問題が発生します。どうすればこれを回避できますか?

4

2 に答える 2

1

を使用する場合CreateAlias(collection)SetFetchMode(collection)効果はありません。

コレクションの積極的な読み込みへのより良いアプローチについては、http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-associations-efficiently-with-nhibernate.aspxを参照してください。

于 2010-11-04T21:43:57.603 に答える
1

ここで何が起こっているのか誤解していると思います...ページングと組み合わせて個別の結果トランスフォーマーを使用することは、ほとんどの場合正しくありません。考えてみてください。上記のクエリでは、外積の最初の20行しか取得できません。リストの最後にあるクライアントのいくつかは、このためにコレクションが入力されておらず、N+1の問題が発生していると思います。

ページング操作を実行する必要がある場合はbatch-size、コレクションマッピングのヒントを使用して、N+1の問題を最小限に抑えることを検討してください。

注:一般的な使用例で一度に20ページを表示する場合は、batch-sizeをこの値に設定します。

于 2010-11-04T18:59:04.610 に答える