2010-06-29 6 views
22

У меня есть требование, чтобы загрузить сложный объект, называемый Node ... ну его не то, что комплекс ... это выглядит следующим образом: -Нетерпеливый Loading Использование Fluent NHibernate/NHibernate & Автоотображение

Узел имеет ссылку на EntityType который имеет один ко многим с собственности, которая в свою очередь, имеет один ко многим с 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() 
    { 
    } 
} 

Что я пытаюсь сделать, это загрузить объект Node со всеми дочерними объектами одновременно. Нет ленивой нагрузки. Причина в том, что у меня есть тысячи объектов Node в базе данных, и я должен отправить их по проводу с помощью службы WCF. Я столкнулся с проблемами SQL N + 1 классов. Я использую Fluent Nhibernate с Automapping и NHibernate Profiler, предложил мне использовать FetchMode.Eager для загрузки всего объекта сразу. Я использую следующий qyuery

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

ИЛИ с помощью NHibernate LINQ

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

Когда я запускаю любой из выше запроса, оба они генерируют один такой же один запрос со всеми левого внешнего соединения, которое является то, что Мне нужно. Однако по какой-то причине возврат запроса ILIS из запроса не загружается в объекты. Infact возвращаемое количество узлов равно количеству строк запроса, поэтому объекты Nodes повторяются. Кроме того, свойства внутри каждого узла повторяются, а также значения Listvalues.

Поэтому я хотел бы знать, как изменить указанный выше запрос, чтобы вернуть все уникальные узлы со свойствами и значениями в них.

Благодаря Набиль

+0

На гугле я узнал о DistinctRootEntityResultTransformer но , которые разрешают проблему только для объектов Root. Я все еще получаю дубликаты в дочерних коллекциях . Каждый корневой объект в возвращаемом списке имеет некоторые странные декартовы беспорядки в дочерних коллекциях с несколькими экземплярами одного и того же объекта. Есть идеи? Ожидание Nabeel – nabeelfarid

+1

Я думаю, что нашел решение, но я хотел бы знать, правильно ли он соответствует . Детские коллекции (EType.Properties, Etype.Properties.ListValues) внутри корневого объекта (Узел) являются IList. И я прочитал в документации , что IList может содержать дубликаты, поэтому, если я изменяю IList на ISet/ ICollection, тогда запрос не загружает дубликаты экземпляров в пределах дочерних коллекций. Но для этого решения требуется много рефакторинга. Я хотел бы знать , если есть способ добиться того же, используя IList для дочерних коллекций? Ожидание, Nabeel – nabeelfarid

+1

У меня такая же проблема (с использованием Fetchmode.Eager). Для этого я довольно разочарован в NHibernate. Я бы предпочел ошибку, чем неверные данные. – UpTheCreek

ответ

13

Я полагаю, что это сам. Ключом является использование SetResultTransformer(), передающий объект DistinctRootEntityResultTransformer в качестве параметра.Таким образом, запрос теперь выглядит следующим образом

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

Я нашел ответ на мои вопросы через эти ссылки:

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, но ничего себе это уродливо. Это похоже на такой беспорядок - я не впечатлен Nibernate для этого. Зачем нам нужно преобразовывать результат? Кажется, что NH не делает работу должным образом. – UpTheCreek

21

каждое отображение должно иметь отложенную загрузку от

в узле Карта:

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

в EnityType Карта:

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

и так далее .. .

Также см NHibernate Eager loading multi-level child objects за один раз жадной загрузки

Добавлены:

Дополнительной информацию о Sql N + 1:

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

+2

Спасибо за ответ Tim, Ну, я не хочу устанавливать .Not.LazyLoad() в сопоставлении, потому что он станет по умолчанию, и в моем приложении у меня есть служба wcf, которая должна передавать данные в клиент, и я хочу загрузить все данные за один раз в одном запросе, чтобы избежать сценария SQL N + 1 (http://nhprof.com/Learn/Alerts/SelectNPlusOne). Остальная часть приложения не требует высокой загрузки. Итак, любая идея, как я могу решить этот сценарий? – nabeelfarid

+1

Также я понимаю, что .Naz.LazyLoad не решает проблему SQL N + 1. Fro mNhibernate profiler Я заметил, что, хотя он загружает весь весь граф объектов за один раз, он по-прежнему генерирует более одного запроса, запрос для каждого объекта reference/hasmany, который я не хочу, потому что у меня есть thosands узлов с хэндерами типы объектов и свойства, и я не хочу, чтобы генерировались уникальные запросы thosands. Nabeel – nabeelfarid

+0

Я думал, что вы хотите, чтобы он был нанесен таким образом. Я добавил ссылку на другой вопрос, который показывает загрузку в конкретном экземпляре. Я думаю, это поможет вам создать соединение. Если нет, вы можете проверить правильность хранимой процедуры и сопоставить ее как именованный запрос. См. Исходный код плакатов в http://stackoverflow.com/questions/1637862/fluent-nhibernate-and-stored-procedures для примера этого –

4

SetResultTransformer с DistinctRootEntityResultTransformer будет работать только для главного объекта, но Коллекции IList будут умножаться.

+0

тот правильный. Нужно использовать ISet или ICollection – nabeelfarid

+0

Как вы будете использовать ISet или ICollection? –

8

я закончил с чем-то вроде этого:

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

Просто убедитесь, чтобы выбрать объект, как это, чтобы избежать дублирования в результате объединения:

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

Можете ли вы сделать это со ссылками? – aggietech

 Смежные вопросы

  • Нет связанных вопросов^_^