2010-12-31 38 views
7

Tôi đã có đồ thị đối tượng này:LINQ to NHibernate ThenFetch nhiều thuộc tính

// 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 
}  

Bây giờ tôi đang thực hiện một truy vấn mà tôi sẽ trả về một danh sách các A s và tôi cần tất cả B s của họ , C s, X s và Y s. Tôi cũng sẽ nhóm chúng bằng B thành tra cứu.

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

Một số điều cần lưu ý:

  1. Đây là một báo cáo trong đó tất cả dữ liệu cần phải được trả lại - kết quả vô biên không phải là một vấn đề.
  2. Tôi đang tạo nhóm bằng cách sử dụng ToLookup vì sử dụng group by có vẻ phức tạp hơn khi bạn cần tất cả giá trị thực - bạn cần phải truy vấn cơ sở dữ liệu cho nhóm và sau đó cho giá trị thực của chúng.

Câu hỏi của tôi là cách chỉ định chiến lược tìm nạp đúng cách. Con đường tôi đã thực hiện nó là cách duy nhất tôi tìm thấy cho này để chạy (có lấy tất cả các bx và giá trị) - nhưng nó tạo ra SQL mà dường như sai:

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 

Như bạn có thể thấy nó nhận được giá trị cho a.b 3 lần - b1b3 để tìm nạp và b6 cho mệnh đề where.

  1. Tôi cho rằng điều này có tác động tiêu cực đến hiệu suất DB - tôi có đúng không?
  2. Có cách nào để sửa đổi các cuộc gọi .Fetch của tôi để chỉ tìm nạp a.b một lần không?
  3. Đây có phải là cách tiếp cận tốt cho vấn đề của tôi không?

Trả lời

4

Nếu bạn thực hiện nhiều lần tìm nạp một đến nhiều thuộc tính trong một truy vấn, bạn sẽ có được một sản phẩm Descartes. NHibernate không xử lý điều này - AFAIK, nó đã được thực hiện cố ý để làm cho nó hoạt động giống như một sự tham gia SQL thực sự. HQL cũng làm như vậy.

Bạn không cần thực hiện tất cả các lần tìm nạp trong một lần. Tách truy vấn và thực thi mỗi lần tìm nạp/ghép một đến nhiều trong một truy vấn riêng biệt. Mỗi bộ nhớ sẽ lưu dữ liệu của nó trong phiên và kết nối tất cả các tham chiếu đối tượng đúng cách. (Lưu ý: Tôi chưa bao giờ thử điều này với LINQ, nhưng nó làm việc trong HQL, và nguyên tắc là như nhau)

Off đỉnh đầu của tôi, nó có thể trông giống như thế này:

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

Có là một trong những tối ưu hơn nữa bạn có thể làm, sử dụng ToFuture() phương pháp thay vì ToList() ... Tôi không chắc chắn làm thế nào nó hoạt động với LINQ và ToLookup phương pháp, nhưng nó không phải là quá khó để có được quyền. ToFuture() sẽ xếp hàng các truy vấn và thực hiện tất cả chúng cùng một lúc thay vì thực hiện các kết nối cơ sở dữ liệu riêng biệt cho mỗi một.

+1

Trước hết, đây là tất cả-một có thể được nhìn thấy từ ánh xạ (chỉ có một B trong A và không phải danh sách), do đó, toàn bộ truy vấn sẽ chỉ tải 4 thực thể. Thứ hai, những gì bạn đang đề xuất là hoàn toàn trái ngược với những gì tôi muốn - thay vì giảm sự phức tạp của truy vấn bạn đã tăng nó bằng cách chia nó thành nhiều truy vấn. Truy vấn lý tưởng sẽ có các tham gia sau đây: 'từ [A] bên ngoài bên trái tham gia [B] trên ...bên ngoài bên trái tham gia [X] trên ... bên ngoài bên trái tham gia [Y] trên ... '. – configurator

+1

Nhận xét trước đó của tôi có vẻ khó chịu; Tôi không có ý định ngăn cản bạn cố gắng giúp đỡ. Tôi _do_ đánh giá cao nỗ lực của bạn, ngay cả khi nó không có vẻ như nó từ bình luận trước đây của tôi. – configurator

+1

Tôi đã kiểm tra hành vi được đề xuất về các thực thể có liên quan trước khi tải. Sử dụng NHibernate Profiler Tôi có thể thấy rằng chúng chỉ được lấy một lần, vì vậy có thể xác nhận rằng những gì bdrajer nói là đúng về bộ nhớ đệm dữ liệu trong phiên. –

Các vấn đề liên quan