Жесткая загрузка с использованием Fluent NHibernate / Nhibernate & Automapping - PullRequest
23 голосов
/ 29 июня 2010

У меня есть требование загрузить сложный объект с именем Узел ... ну, это не так сложно ... это выглядит следующим образом: -

A Узел имеет ссылку на 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. Я столкнулся с проблемой классов N + 1 SQL. Я использую 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" )

Когда я запускаю любой из вышеприведенных запросов, они оба генерируют один и тот же одиночный запрос со всеми левыми внешними объединениями, что мне и нужно. Однако по какой-то причине возвращаемый IList из запроса не является загружаемым свойством в объекты. На самом деле количество возвращаемых узлов равно количеству строк запроса, поэтому объекты узлов повторяются. Более того, свойства в каждом узле повторяются, как и значения списков.

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

Спасибо Набиль

Ответы [ 4 ]

22 голосов
/ 29 июня 2010

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

в Node Map:

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

в EnityType Map:

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

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

Также см. NHibernate Eager, загружающий многоуровневые дочерние объекты для одноразовой загрузки

Добавлено:

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

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

13 голосов
/ 18 августа 2010

Я сам разбираюсь.Ключ должен использовать 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/nhusers@googlegroups.com/2010-05/msg00512.html

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

9 голосов
/ 12 октября 2011

У меня получилось что-то вроде этого:

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

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

session.CreateCriteria(typeof(T)).SetResultTransformer(Transformers.DistinctRootEntity).List<T>();
4 голосов
/ 21 декабря 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...