为什么NHibernate不抓取我的数据

为什么NHibernate不抓取我的数据,nhibernate,eager-loading,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"> <

我正在使用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>

因此,我相信除非另有说明,否则它将延迟加载控件的控件详细信息

我正在运行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;
}
public T GetById(Int32 id,List fetch),其中T:BaseObject
{
T retObj=null;
ISession session=ensureRecurrentSession();
{
ICriteria-criteria=session.CreateCriteria(typeof(T));
条件。可设置缓存(true);
添加(表达式.Eq(“Id”,Id));
foreach(提取中的变量toFetch)
{
SetFetchMode(toFetch,FetchMode.Eager);
}
retObj=criteria.List().FirstOrDefault();
}
返回retObj;
}
*注意:我不喜欢如何设置存储库,但它是在我进入项目之前完成的,所以我们现在必须坚持这种模式

我这样称呼这个方法

public Control GetByIDWithDetail(int controlID)
{
    return DataRepository.Instance.GetById<Control>(controlID, new List<string>() {"ControlDetail"});
}
公共控件GetById WithDetail(int-controlID)
{
返回DataRepository.Instance.GetById(controlID,new List(){“ControlDetail”});
}
当我调试GetByID方法并查看retObj时,我可以看到ControlDetails列表已经填充(尽管奇怪的是,我还注意到,如果没有setfetchmode设置,列表已经填充)

即使使用此修复程序,NHProf也会通过以下行识别Select N+1问题

List<ControlDetail> details = control.ControlDetails.ToList();
List details=control.ControlDetails.ToList();
我到底遗漏了什么?我如何停止这个N+1,但仍然在controlDetails列表上迭代

编辑:xml配置看起来是这样的(稍微编辑以使其更小)


还有这个


一个选项,您可以减少选择n+1问题,保持延迟加载,并忘记快速加载

您只需向XML添加一个简单的属性
batch size

<bag name="ControlDetails" batch-size="25" ..>


我还注意到,您的映射中有
lazy=“true”
,您是否尝试更改为false?

即时抓取和即时加载之间有很大区别。取数的意思是:放入同一个查询。它有几个副作用,可能会破坏它。急切加载意味着强制NH立即加载它,而不是等到第一次访问它。它仍然使用其他查询加载,这导致了N+1问题

NH可能不急于获取,因为该属性名为
ControlDetails
,但您将
ControlDetail
作为参数传递


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

最有可能的是“控件”来自一级缓存,但关系是延迟加载的,因为关系未设置为与父级缓存。抓取NHProf并查看缓存命中与查询。NHProf在查询缓存命中/未命中/放置计数中显示为0,在二级缓存命中/未命中/放置计数中显示为0,我将如何解决此问题?控件和控件详细信息的命中率都是0吗?或者只是ControlDetail?哦,实际上,我认为它也不会缓存,因为您的映射没有设置为缓存。Hmmm@mathieu:没错。并尝试在同一查询中获取多个集合。有一个DistinctCollectionItemTransformer,但没有“DistinctCollectionItemTransformer”。一切都会成倍增长。我还遇到了一些奇怪的问题,查询中的列太多了。我真的停止使用它了。
<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>
<bag name="ControlDetails" batch-size="25" ..>