Высокая загрузка с использованием Fluent NHibernate/Nhibernate & Automapping
У меня есть требование загрузить сложный объект с именем Node... ну его не такой сложный... он выглядит следующим образом: -
A Node имеет ссылку на 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 повторяются. Кроме того, свойства внутри каждого Node повторяются, а также значения Listvalues.
Итак, я хотел бы знать, как изменить указанный выше запрос, чтобы вернуть все уникальные узлы со свойствами и значениями в них.
Спасибо
Набиль
Ответы
Ответ 1
Я сам это понял. Ключ должен использовать 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
Ответ 2
каждое отображение должно иметь ленивую загрузку
в Node Карта:
Map(x => x.EntityType).Not.LazyLoad();
в карте EnityType:
Map(x => x.Properties).Not.LazyLoad();
и т.д.
Кроме того, см. NHibernate Eager загружает многоуровневые дочерние объекты для одноразовой загрузки
Добавлено:
Дополнительная информация о Sql N + 1:
http://nhprof.com/Learn/Alerts/SelectNPlusOne
Ответ 3
У меня получилось что-то вроде этого:
HasMany(x => x.YourList).KeyColumn("ColumnName").Inverse().Not.LazyLoad().Fetch.Join()
Просто убедитесь, что вы выбрали свой объект таким образом, чтобы избежать дублирования из-за соединения:
session.CreateCriteria(typeof(T)).SetResultTransformer(Transformers.DistinctRootEntity).List<T>();
Ответ 4
SetResultTransformer с DistinctRootEntityResultTransformer будет работать только для основного объекта, но коллекции IList будут умножены.