Я столкнулся со странной проблемой NHibernate и Automapper. Я не уверен, кто виноват, но я борюсь целый день и не могу понять, почему.
Вот мои файлы сопоставления Nhibernate:
Navigation.hbm.xml
<id name="ID" column="NavigationID">
<generator class="identity"></generator>
</id>
<property name="IsDefault"/>
<property name="RoleType" column="RoleTypeID" />
<bag name="Items" cascade="save-update" inverse="true" lazy="false" fetch="join">
<key column="NavigationID"/>
<one-to-many class="NavigationItem"/>
</bag>
NavigationItem.hbm.xml
<class name="NavigationItem" table="NavigationItem">
<id name="ID" column="NavigationItemID">
<generator class="identity"></generator>
</id>
<property name="ShowInMenu"/>
<property name="Order" column="[Order]" />
<many-to-one name="Page" column="PageID" lazy="false" fetch="join" />
<many-to-one name="Navigation" column="NavigationID" />
<many-to-one name="Parent" column="ParentNavigationItemID" />
<bag name="Items" cascade="save-update" inverse="true">
<key column="ParentNavigationItemID"/>
<one-to-many class="NavigationItem"/>
</bag>
This is how I fill up a Navigation object:
ISession session = SessionProvider.Instance.CurrentSession;
using (transaction = session.BeginTransaction())
{
var navigation = session.QueryOver<Navigation>()
.Where(x => x.IsDefault && x.RoleType == null)
.TransformUsing(new NHibernate.Transform.RootEntityResultTransformer())
.SingleOrDefault();
transaction.Commit();
return navigation;
}
Поскольку для сумки Items объекта Navigation установлено значение lazy = "false", я получаю только один запрос к базе данных, чтобы получить объект Navigation, и левое соединение, чтобы также получить все элементы Navigation.
До сих пор все идеально.
Я провел тест, чтобы перебрать все элементы и подпункты рекурсивно, и больше никаких обращений к базе данных.
Затем у меня есть модель пользовательского интерфейса, которую я отображаю с помощью Automapper.
Вот модели пользовательского интерфейса:
public class NavigationModel
{
public List<NavigationItemModel> Items { get; set; }
public NavigationModel()
{
Items = new List<NavigationItemModel>();
}
}
public class NavigationItemModel
{
public string PageName { get; set; }
public string Url { get; set; }
public bool Selected { get; set; }
public NavigationItemModel Parent { get; set; }
public List<NavigationItemModel> Items { get; set; }
}
И сопоставления автомата:
AutoMapper.Mapper
.CreateMap<NavigationItem, NavigationItemModel>()
// IF I REMOVE THE NEXT LINE, IT HITS THE DATABASE FOR EACH SUB-ITEM of the NavigationItem.Items
.ForMember(m => m.Items, o => o.Ignore());
AutoMapper.Mapper
.CreateMap<Navigation, NavigationModel>();
Хорошо, теперь поведение такое:
- Если я проигнорирую элемент NavigationItem.Items в сопоставлении, все будет хорошо, но отображаются только элементы Navigation и его элементы. Коллекции вложенных элементов элементов навигации не отображаются. НО база данных больше не поражается. Но я хочу, чтобы и другие элементы были нанесены на карту ...
- Если я удалю строку под комментарием, база данных будет поражена для каждого из Navigation.Items, запрашивая его подпункты (где ParentID = Item.ID).
Есть идеи, что я делаю не так?
Извините за стену с текстом, но я подумал, что лучше описать ее более подробно, я потратил на нее целый день и перепробовал всевозможные запросы с Future, JoinQueryOver и т. Д. Проблема не связана с NHibernate, так как это загружается нормально, и я могу выполнять итерацию без дополнительных обращений к базе данных.
Я забыл включить генерируемый SQL:
Сначала такой запрос:
SELECT this_.NavigationID as Navigati1SELECT items0_.ParentNavigationItemID as ParentNa6_2_,
items0_.NavigationItemID as Navigati1_2_,
items0_.NavigationItemID as Navigati1_4_1_,
items0_.ShowInMenu as ShowInMenu4_1_,
items0_.[Order] as column3_4_1_,
items0_.PageID as PageID4_1_,
items0_.NavigationID as Navigati5_4_1_,
items0_.ParentNavigationItemID as ParentNa6_4_1_,
page1_.PageID as PageID8_0_,
page1_.Name as Name8_0_,
page1_.Title as Title8_0_,
page1_.Description as Descript4_8_0_,
page1_.URL as URL8_0_
FROM NavigationItem items0_
left outer join Page page1_
on items0_.PageID = page1_.PageID
WHERE items0_.ParentNavigationItemID = 1 /* @p0 */
2_,
this_.IsDefault as IsDefault7_2_,
this_.RoleTypeID as RoleTypeID7_2_,
items2_.NavigationID as Navigati5_4_,
items2_.NavigationItemID as Navigati1_4_,
items2_.NavigationItemID as Navigati1_4_0_,
items2_.ShowInMenu as ShowInMenu4_0_,
items2_.[Order] as column3_4_0_,
items2_.PageID as PageID4_0_,
items2_.NavigationID as Navigati5_4_0_,
items2_.ParentNavigationItemID as ParentNa6_4_0_,
page3_.PageID as PageID8_1_,
page3_.Name as Name8_1_,
page3_.Title as Title8_1_,
page3_.Description as Descript4_8_1_,
page3_.URL as URL8_1_
FROM Navigation this_
left outer join NavigationItem items2_
on this_.NavigationID = items2_.NavigationID
left outer join Page page3_
on items2_.PageID = page3_.PageID
WHERE (this_.IsDefault = 1 /* @p0 */
and this_.RoleTypeID is null)
Затем, когда в игру вступает Automapper, создается список этих запросов, отличается только параметр p0 (от 1 до 12 ... количество элементов без родителей)
SELECT items0_.ParentNavigationItemID as ParentNa6_2_,
items0_.NavigationItemID as Navigati1_2_,
items0_.NavigationItemID as Navigati1_4_1_,
items0_.ShowInMenu as ShowInMenu4_1_,
items0_.[Order] as column3_4_1_,
items0_.PageID as PageID4_1_,
items0_.NavigationID as Navigati5_4_1_,
items0_.ParentNavigationItemID as ParentNa6_4_1_,
page1_.PageID as PageID8_0_,
page1_.Name as Name8_0_,
page1_.Title as Title8_0_,
page1_.Description as Descript4_8_0_,
page1_.URL as URL8_0_
FROM NavigationItem items0_
left outer join Page page1_
on items0_.PageID = page1_.PageID
WHERE items0_.ParentNavigationItemID = 1 /* @p0 */
Это взято из приложения NHProf, надеюсь, это поможет.
Спасибо, Космин