Почему NHibernate не хочет получать мои данные - PullRequest
2 голосов
/ 03 августа 2011

Я использую 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 Control GetByIDWithDetail(int controlID)
{
    return DataRepository.Instance.GetById<Control>(controlID, new List<string>() {"ControlDetail"});
}

Когда я отлаживаю метод GetByID и смотрю на retObj, я вижу, что список ControlDetails заполнен (хотя, как ни странно, я также заметил, что без набора setfetchmode список заполняется)

Даже с этим исправлением NHProf выявляет проблему Select N + 1 со следующей строкой

List<ControlDetail> details = control.ControlDetails.ToList();

Что именно мне не хватает и как я могу остановить это 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>

и это

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

Ответы [ 2 ]

5 голосов
/ 03 августа 2011

Существует значительная разница между энергичной загрузкой и активной загрузкой. Получение означает: положить в тот же запрос. Он имеет несколько побочных эффектов и может сломать его. Стремительная загрузка означает, что NH должен немедленно загрузить его, не дожидаясь, пока к нему не получат доступ в первый раз. Он по-прежнему загружается с использованием дополнительных запросов, что приводит к проблеме N + 1.

NH, вероятно, не охотно выбирает, потому что свойство называется ControlDetails, но вы передаете ControlDetail в качестве аргумента.

С другой стороны, это не хороший способ избежать проблемы N + 1. Вместо этого используйте пакетный размер. Он полностью прозрачен для приложения и уменьшает количество запросов по данному фактору (значения от 5 до 50 имеют смысл, используйте 10, если вы не знаете, что использовать).

1 голос
/ 03 августа 2011

Один вариант, вы можете уменьшить проблему выбора n + 1, сохранить ленивую загрузку и забыть о нетерпеливой загрузке ....

Все, что вам нужно сделать, это добавить один простой атрибут batch-size в ваш XML

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

Я также заметил, что в вашем отображении lazy="true", вы пытались изменить на false?

...