为什么NHibernate不急于获取我的数据

Dan*_*ell 2 nhibernate eager-loading

我正在使用Nhibernate作为我的ORM.

我有一个"Control"类,它与ControlDetail有一对多的关系(即一个控件有很多controlDetails).

在控件xml配置中,它具有以下内容

<bag name="ControlDetails" lazy="true" access="property" order-by="SortOrder asc"  cascade="all-delete-orphan" 
  table="ControlDetail">
  <key column="ControlID"/>
  <one-to-many class="ControlDetail"/>
</bag>
Run Code Online (Sandbox Code Playgroud)

这样我相信除非另有说明,它会延迟加载控件的控件.

我正在运行NHProf来尝试解决我们遇到的一些性能问题,它已经在这些类中找到了一个Select N + 1问题.

我们正在使用存储库DA层,我试图看看我是否可以在需要时添加以急切获取数据的方式并提出这个.

public T GetById<T>(Int32 id, List<string> fetch) where T : BaseObject
{
    T retObj = null;
    ISession session = EnsureCurrentSession();
    {
        ICriteria criteria = session.CreateCriteria(typeof (T));
        criteria.SetCacheable(true);
        criteria.Add(Expression.Eq("Id", id));

        foreach(var toFetch in fetch)
        {
            criteria.SetFetchMode(toFetch, FetchMode.Eager);
        }

        retObj = criteria.List<T>().FirstOrDefault();
    }

    return retObj;
}
Run Code Online (Sandbox Code Playgroud)

*注意:我不喜欢存储库是如何设置的,但它是在我进入项目之前完成的,所以我们现在必须坚持使用这种模式.

我这样称呼这个方法

public Control GetByIDWithDetail(int controlID)
{
    return DataRepository.Instance.GetById<Control>(controlID, new List<string>() {"ControlDetail"});
}
Run Code Online (Sandbox Code Playgroud)

当我调试GetByID方法并查看retObj时,我可以看到已经填充了ControlDetails列表(虽然奇怪的是我还注意到没有setfetchmode设置列表正在填充)

即使使用此修复,NHProf也会使用以下行标识选择N + 1问题

List<ControlDetail> details = control.ControlDetails.ToList();
Run Code Online (Sandbox Code Playgroud)

究竟我错过了什么以及如何停止此N + 1但仍然遍历controlDetails列表

编辑:xml配置看起来像这样(稍微编辑,以使更小)

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DomainObjects" assembly="DomainObjects">
    <class name="Control" lazy="false" table="Control" optimistic-lock="version" select-before-update="true"  >
        <id name="Id" type="int" column="ControlID" access="property">
            <generator class="native" />
        </id>
    <version name="Version" column="Version" />
        <property name="AdministrativeControl" column="AdministrativeControl" access="property" not-null="true" />
    <property name="Description" column="ControlDescription" access="property" />
    <property name="Title" column="Title" access="property" />
    <property name="CountOfChildControls" access="property" formula="(select count(*) from Control where Control.ParentControlID = ControlID)" />

    <bag name="ControlDetails" lazy="true" access="property" order-by="SortOrder asc"  cascade="all-delete-orphan"
      table="ControlDetail">
      <key column="ControlID" />
      <one-to-many class="ControlDetail"  />
    </bag>

  </class>
</hibernate-mapping>
Run Code Online (Sandbox Code Playgroud)

还有这个

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DomainObjects" assembly="DomainObjects">
    <class name="ControlDetail" lazy="false" table="ControlDetail" select-before-update="true" optimistic-lock="version">
        <id name="Id" type="int" column="ControlDetailID" access="property">
            <generator class="native" />
        </id>
    <version name="Version" column="Version" />
    <property name="Description" column="Description" access="property" not-null="true" />
    <property name="Title" column="Title" access="property" />

    <many-to-one name="Control" lazy="false" class="Control" column="ControlID" access="property"/>
  </class>
</hibernate-mapping>
Run Code Online (Sandbox Code Playgroud)

Ste*_*ger 5

渴望获取和急切加载之间存在很大差异.提取意味着:进入相同的查询.它有几个副作用,可能会打破它.急切加载意味着迫使NH立即加载它,而不是等到第一次访问它.它仍然使用其他查询加载,这会导致N + 1问题.

NH可能不会急切地获取因为属性被调用ControlDetails,但是你ControlDetail作为参数传递.

另一方面,这不是避免N + 1问题的好方法.请改用批量大小.它对应用程序完全透明,并通过给定因子减少查询量(从5到50的值是有意义的,如果您不知道使用什么,请使用10).