2010-06-29 33 views
22

Tôi có yêu cầu để tải một đối tượng phức tạp gọi là Node ... nó cũng không phải là phức tạp ... nó trông giống như sau: -tải Háo hức Sử dụng thành thạo NHibernate/Nhibernate & automapping

Một Node có một tham chiếu đến EntityType trong đó có một một đến nhiều với tài sản mà lần lượt có một một đến nhiều với PorpertyListValue

public class Node 
{ 
    public virtual int Id 
    { 
     get; 
     set; 
    } 

    public virtual string Name 
    { 
     get; 
     set; 
    } 

    public virtual EntityType Etype 
    { 
     get; 
     set; 
    } 

} 


public class EntityType 
{ 
    public virtual int Id 
    { 
     get; 
     set; 
    } 

    public virtual string Name 
    { 
     get; 
     set; 
    } 

    public virtual IList<Property> Properties 
    { 
     get; 
     protected set; 
    } 

    public EntityType() 
    { 
     Properties = new List<Property>(); 
    } 
} 

public class Property 
{ 
    public virtual int Id 
    { 
     get; 
     set; 
    } 

    public virtual string Name 
    { 
     get; 
     set; 
    }   

    public virtual EntityType EntityType 
    { 
     get; 
     set; 
    } 

    public virtual IList<PropertyListValue> ListValues 
    { 
     get; 
     protected set; 
    } 

    public virtual string DefaultValue 
    { 
     get; 
     set; 
    } 

    public Property() 
    { 
     ListValues = new List<PropertyListValue>(); 
    } 
} 


public class PropertyListValue 
{ 
    public virtual int Id 
    { 
     get; 
     set; 
    } 

    public virtual Property Property 
    { 
     get; 
     set; 
    } 

    public virtual string Value 
    { 
     get; 
     set; 
    } 

    protected PropertyListValue() 
    { 
    } 
} 

Điều tôi cố gắng làm là tải đối tượng Nút bằng tất cả các đối tượng con cùng một lúc. Không tải trọng. Lý do là tôi có hàng ngàn đối tượng Node trong cơ sở dữ liệu và tôi phải gửi chúng qua dây bằng cách sử dụng WCF Service.I chạy vào lớp SQL N + 1 vấn đề. Tôi đang sử dụng Fluent Nhibernate với Automapping và NHibernate Profiler đề nghị tôi sử dụng FetchMode.Eager để tải toàn bộ các đối tượng cùng một lúc. Tôi đang sử dụng qyuery sau

 Session.CreateCriteria(typeof (Node)) 
      .SetFetchMode("Etype", FetchMode.Join) 
      .SetFetchMode("Etype.Properties", FetchMode.Join) 
      .SetFetchMode("Etype.Properties.ListValues", FetchMode.Join) 

HOẶC sử dụng NHibernate LINQ

 Session.Linq<NodeType>() 
     .Expand("Etype") 
     .Expand("Etype.Properties") 
     .Expand("Etype.Properties.ListValues") 

Khi tôi chạy bất kỳ truy vấn trên, cả hai đều tạo ra một truy vấn duy nhất cùng với tất cả các bên ngoài trái tham gia, đó là những gì Tôi cần. Tuy nhiên, đối với một số lý do, IList trả về từ truy vấn không được nạp thuộc tính vào các đối tượng. Infact số nút trả về bằng số hàng của truy vấn, do đó, các đối tượng nút được lặp lại. Hơn nữa, các thuộc tính trong mỗi nút được lặp lại, và do đó, làm các giá trị danh sách.

Vì vậy, tôi muốn biết cách sửa đổi truy vấn trên để trả lại tất cả các nút duy nhất với các giá trị thuộc tính và danh sách bên trong chúng.

Cảm ơn Nabeel

+0

Vào google tôi phát hiện ra về DistinctRootEntityResultTransformer nhưng chỉ giải quyết vấn đề cho đối tượng Gốc. Tôi vẫn nhận được các bản sao trong bộ sưu tập con. Mỗi đối tượng gốc trong danh sách trả về có một số lộn xộn sản phẩm Cartesian kỳ lạ trong bộ sưu tập con với nhiều phiên bản của cùng một thực thể. Bất kỳ ý tưởng? Đang chờ Nabeel – nabeelfarid

+1

Tôi nghĩ rằng tôi đã tìm thấy giải pháp nhưng tôi muốn biết nếu số chính xác của nó. Bộ sưu tập con (EType.Properties, Etype.Properties.ListValues) bên trong đối tượng gốc (Nút) là IList. Và tôi đọc trong tài liệu rằng IList có thể chứa các bản sao, vì vậy nếu tôi thay đổi IList thành ISet/ ICollection, thì truy vấn không tải các bản sao trùng lặp trong các bộ sưu tập con . Nhưng giải pháp này đòi hỏi rất nhiều việc tái cấu trúc. Tôi muốn biết nếu có cách nào để đạt được điều tương tự bằng cách sử dụng IList cho trẻ em bộ sưu tập? Đang chờ, Nabeel – nabeelfarid

+1

Tôi gặp vấn đề tương tự (sử dụng Fetchmode.Eager). Tôi khá thất vọng trong NHibernate cho việc này. Tôi thà có lỗi hơn dữ liệu không chính xác. – UpTheCreek

Trả lời

13

Tôi hình dung nó ra bản thân mình. Điều quan trọng là sử dụng SetResultTransformer() chuyển đối tượng DistinctRootEntityResultTransformer làm tham số.Vì vậy, các truy vấn bây giờ trông giống như như sau

Session.CreateCriteria(typeof (Node)) 
    .SetFetchMode("Etype", FetchMode.Join) 
    .SetFetchMode("Etype.Properties", FetchMode.Join) 
    .SetFetchMode("Etype.Properties.ListValues", FetchMode.Join) 
    .SetResultTransformer(new DistinctRootEntityResultTransformer()); 

Tôi tìm thấy câu trả lời cho câu hỏi của mình thông qua những liên kết này:

http://www.mailinglistarchive.com/html/[email protected]/2010-05/msg00512.html

http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-associations-efficiently-with-nhibernate.aspx

+4

+1, nhưng Wow thật xấu. Điều này chỉ có vẻ như một mớ hỗn độn - Tôi không ấn tượng với Nibernate cho việc này. Tại sao chúng ta cần phải chuyển đổi kết quả? Có vẻ như NH không làm đúng công việc của nó. – UpTheCreek

21

từng lập bản đồ phải có tải lười biếng tắt

trong Node Bản đồ:

Map(x => x.EntityType).Not.LazyLoad(); 

trong EnityType Bản đồ:

Map(x => x.Properties).Not.LazyLoad(); 

và vân vân .. .

Ngoài ra, xem NHibernate Eager loading multi-level child objects cho một thời gian tải háo hức

Added:

Thông tin thêm về Sql N + 1:

http://nhprof.com/Learn/Alerts/SelectNPlusOne

+2

Cảm ơn phản ứng Tim, Tôi không muốn đặt .Not.LazyLoad() trong ánh xạ vì nó sẽ trở thành hành vi mặc định và trong ứng dụng của tôi, tôi có dịch vụ wcf cần truyền dữ liệu đến khách hàng và tôi muốn tải tất cả dữ liệu cùng một lúc trong một truy vấn để tránh trường hợp SQL N + 1 (http://nhprof.com/Learn/Alerts/SelectNPlusOne). Phần còn lại của ứng dụng không yêu cầu nạp dữ liệu. Vì vậy, bất kỳ ý tưởng làm thế nào tôi có thể giải quyết kịch bản này? – nabeelfarid

+1

Ngoài ra sự hiểu biết của tôi là .Not.LazyLoad không giải quyết được vấn đề SQL N + 1. Fro mNhibernate profiler Tôi đã nhận thấy rằng mặc dù nó tải toàn bộ đồ thị đối tượng trong một lần, nó vẫn tạo ra nhiều truy vấn, một truy vấn cho mỗi đối tượng tham chiếu/hasmany, mà tôi không muốn bởi vì tôi có thosands các nút với hundered entitytypes và các thuộc tính và tôi không muốn tạo ra các truy vấn độc đáo thosands. Nabeel – nabeelfarid

+0

Tôi nghĩ bạn muốn nó được lập bản đồ theo cách đó. Tôi đã thêm một liên kết đến một câu hỏi khác cho thấy tải mong muốn trong một trường hợp cụ thể. Tôi nghĩ rằng điều này sẽ giúp bạn tạo ra một tham gia. Nếu không, bạn có thể xem xét việc thực hiện một thủ tục lưu sẵn và ánh xạ nó như một truy vấn được đặt tên. Xem mã áp phích gốc trong http://stackoverflow.com/questions/1637862/fluent-nhibernate-and-stored-procedures để biết ví dụ về số –

4

SetResultTransformer với DistinctRootEntityResultTransformer sẽ chỉ làm việc cho đối tượng chính nhưng Các bộ sưu tập IList sẽ được nhân lên.

+0

đúng. Người ta phải sử dụng ISet hoặc ICollection – nabeelfarid

+0

Bạn sẽ sử dụng ISet hoặc ICollection như thế nào? –

8

tôi đã kết thúc với một cái gì đó như thế này:

HasMany(x => x.YourList).KeyColumn("ColumnName").Inverse().Not.LazyLoad().Fetch.Join() 

Chỉ cần chắc chắn để chọn đơn vị bạn như thế này, để tránh trùng lặp do tham gia:

session.CreateCriteria(typeof(T)).SetResultTransformer(Transformers.DistinctRootEntity).List<T>(); 
+0

Bạn có thể làm điều này với các tham chiếu không? – aggietech

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