2010-12-31 16 views
7

bu nesne grafiği var: ArtıkLinq NHibernate ThenFetch birden fazla mülke

// Lots of stuff omitted for brevity; these are all virtual properties and there 
// are other properties which aren't shown on all classes. 
class A { 
    B b; 
    C c; 
    DateTime timestamp; 
} 
class B { 
    X x; 
    Y y; 
} 
class X { 
    int id; 
} 
class C { } 
class Y { } 

or to put it more simply, 
a = { 
    b: { 
     x { id: int }, 
     y: { } 
    }, 
    c: { }, 
    timestamp: DateTime 
}  

ben A s listesini döndürmek için gidiyorum bir sorgu yapıyorum ve tüm bunların B s ihtiyaç , C s, X s ve Y s. Ayrıca onları B tarafından bir aramada gruplayacağım.

ILookup<B, A> GetData(List<int> ids) { 
    using (ISession session = OpenSession()) { 
     var query = from a in session.Query<A>() 
        where ids.Contains(a.b.x.id) 
        orderby A.timestamp descending 
        select a; 

     query = query 
      .Fetch(a => a.b) 
      .ThenFetch(b => b.x) 
      .Fetch(a => a.b) 
      .ThenFetch(b => b.y) 
      .Fetch(a => a.c); 

     return query.ToLookup(a => a.b); 
    } 
} 

birkaç nokta not:

  1. Bu, tüm veriler iade edilmesi gereken bir rapordur - sınırsız sonuçları bir sorun değildir.
  2. ToLookup kullanarak gruplamayı yapıyorum çünkü group by kullanmak, tüm gerçek değerlere ihtiyaç duyduğunuzda daha karmaşık görünüyor; bu nedenle, grupların veritabanını ve daha sonra gerçek değerleri için sorgulamanız gerekir.

Sorum düzgün getiriliyor stratejisini belirlemek için nasıl. Bunu yaptık yolu bu (bütün bx ve değerleriyle getirilen sahip) çalıştırmak için bulunan tek yoldur - ama yanlış görünüyor SQL üretir: Eğer gidiyor görebileceğiniz gibi

select /* snipped - every mapped field from a0, b1, x2, b3, y4, c5 - but not b6 */ 
from  [A] a0 
     left outer join [B] b1 
      on a0.B_id = b1.BId 
     left outer join [X] x2 
      on b1.X_id = x2.XId 
     left outer join [B] b3 
      on a0.B_id = b3.BId 
     left outer join [Y] y4 
      on b3.Y_id = y4.YId 
     left outer join [C] c5 
      on a0.C_id = c5.CId, 
     [B] b6 
where a0.B_id = b6.BId 
     and (b6.X_id in (1, 2, 3, 4, 5)) 
order by a0.timestamp desc 

getirme için a.b 3 kez - b1 ve b3 için değer ve nerede yan tümce için b6.

  1. Bunun, DB performansı üzerinde olumsuz bir etkisi olduğunu varsayalım - doğru muyum? yalnızca bir kez a.b getirir yani benim .Fetch aramaları değiştirmek için bir yol
  2. var mı?
  3. Bu benim sorunuma iyi bir yaklaşım mı?

cevap

4

Bir sorguda birden çok özellik birden çok getirme yaparsanız, kartezyen bir ürün elde edersiniz. NHibernate bunu işlemez - AFAIK, gerçek bir SQL katılımı gibi davranmasını sağlamak için bilerek yapıldı. HQL aynı şeyi yapar.

Hepiniz tek seferde getirir yapmak gerekmez

. Sorguyu ayırın ve her birinden çoğa getir/ayrı bir sorguda birleştirin. Her biri, verilerini oturumda önbelleğe alır ve tüm nesne referanslarını doğru şekilde bağlar. şöyle bir şey görünebilir, kafamın üst Kapalı

: (Ben LINQ ile bu hiç denemedim, ama HQL içinde çalışır ve prensip aynıdır Not):

ILookup<B, A> GetData(List<int> ids) { 
using (ISession session = OpenSession()) { 
    var query = from a in session.Query<A>() 
       where ids.Contains(a.b.x.id) 
       orderby A.timestamp descending 
       select a; 

    query 
     .Fetch(a => a.b) 
     .ThenFetch(b => b.x) 
     .ToList(); 
    query 
     .Fetch(a => a.b) 
     .ThenFetch(b => b.y) 
     .Fetch(a => a.c) 
     .ToList(); 

    return query.ToLookup(a => a.b); 
} 

Orada Yapabileceğiniz bir başka optimizasyon ise, ToList() yerine ToFuture() yöntemini kullanır ... LINQ ve ToLookup yöntemleriyle nasıl çalıştığından emin değilim, ancak doğru olması çok zor olmamalı. ToFuture(), her biri için ayrı veritabanı bağlantıları yapmak yerine sorguları sıraya koyacak ve hepsini bir kerede yürütecektir. Her şeyden

+1

Birincisi, bunların hepsi birçok bire haritalama görülebilir gibidir (orada sadece birinin bir A'da B ve onlardan bir listesi), bu yüzden tüm sorgu sadece olacak yük 4 kişiler. İkincisi, önerdiğiniz şey tam olarak istediğimin tam tersidir - sorgunun karmaşıklığını azaltmak yerine onu birden fazla sorguya bölerek artırdınız. İdeal sorgu aşağıdaki birleşimlere sahip olacak: 'dan [A] sol dış birleştirmeden [B] ...sol dış birleşim [X] on ... sol dıştaki birleşim [Y] on ... '. – configurator

+1

Bir önceki yorumum ses geliyor sert; Sana yardım etmeye çalışmaktan caydırmak istemedim. Önceki yorumumdan hoşlanmasa bile, gayretinizi takdir ediyorum. – configurator

+1

Ön yükleme ile ilgili varlıklar hakkında önerilen davranışı kontrol ettim. NHibernate Profiler'i Kullanarak bunların sadece bir kez alındığını görebiliyorum, bu yüzden bdrajer'ın oturumdaki verileri önbelleğe alma hakkında doğru olduğunu söylediğini doğrulayabiliriz. –

İlgili konular