当 Automapper 遍历时,NHibernate 急切加载的树会命中数据库

发布于 2024-11-26 19:58:43 字数 5364 浏览 2 评论 0原文

我遇到了一个奇怪的 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>

这是我填充导航对象的方式:

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 bag 设置为lazy="false",所以我只得到一个查询数据库用于获取导航对象,左连接也用于获取所有导航项。

到目前为止一切都很完美。

我做了一个测试来递归遍历所有项目和子项目,并且不再对数据库进行命中。

然后,我有一个使用 Automapper 映射的 UI 模型。

以下是 UI 模型:

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.Items 访问数据库,查询其子项目(其中 ParentID = Item.ID)。

知道我做错了什么吗?

抱歉,文字墙很长,但我认为最好更详细地描述它,我花了一整天的时间在这个问题上,并尝试使用 Future 和 JoinQueryOver 等进行各种查询。问题似乎不在于 NHibernate,因为加载得很好,我可以迭代而无需再调用数据库。


我忘记包含正在生成的 SQL:

首先是这个查询:

SELECT this_.NavigationID             as Navigati1_7_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应用程序中获取的,希望它有所帮助。

谢谢你, 科斯明

I ran into a strange NHibernate and Automapper problem. I am not sure which one is to blame but I am struggling for a whole day now and I can't seem to find out why.

Here is my Nhibernate mapping files:

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;
            }

Since the Items bag on the Navigation object is set to lazy="false", I get only one query to the database to get the Navigation object and a left join to get all the Navigation items as well.

All is perfect until now.

I did a test to iterate through all the items and the sub-items recursive and no more hits to the database.

Then, I have an UI model that I map with Automapper.

Here are the UI models:

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; }
}

And the automapper mappings:

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>();

Ok, now the behavior is like this:

  • If I ignore the NavigationItem.Items member in the mapping, all goes well, but only the Navigation and it's items are mapped. No sub-items collection of the navigation's Items are mapped. BUT the database is not hit anymore. But I want the other items mapped as well...
  • If I remove the line under the comment, the database is hit for each of the Navigation.Items, querying for it's sub-items (where ParentID = Item.ID).

Any idea what am I doing wrong?

Sorry for the wall of text, but I thought better to describe it in more detail, I spent the whole day on this one and I tried all kind of queries with Future and JoinQueryOver, etc. The problem does not seem to be with NHibernate since that loads fine and I can iterate without any more calls to the database.


I forgot to include the SQL that is being generated:

First there is this query:

SELECT this_.NavigationID             as Navigati1_7_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)

Then, when Automapper comes into play, a list of these queries are being generated, only the p0 parameter differs (from 1 to 12 ... the number of items without parents )

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 */

This is taken from the NHProf application, hope it helps.

Thank you,
Cosmin

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

方圜几里 2024-12-03 19:58:43

我认为 AutoMapper 正在递归地映射你的类。如果是这种情况,您可以使用以下命令指定映射的最大深度

Mapper.CreateMap<TSource, TDestination>().MaxDepth(2); // or 1, or 3, or whatever

I think AutoMapper is mapping your classes recursively. If this is the case, than you can specifiy the max depth for your mappings using

Mapper.CreateMap<TSource, TDestination>().MaxDepth(2); // or 1, or 3, or whatever
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文