Отказ от выбора N + 1 с использованием бизнес-правил в NHibernate - PullRequest
2 голосов
/ 16 июня 2009

Я знаю основные способы избежать проблемы выбора N + 1 в Hibernate / NHibernate, но столкнулся с вариантом проблемы, для которого я не могу найти хорошее решение.

У меня сопоставлены следующие три объекта: пункт, категория и клиент. Элемент связан со многими ко многим для Категории, а Категория сопоставлена ​​со многими к одному Клиенту. Пока ничего особенного.

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

ICriteria criteria = mySession.CreateCriteria(typeof(Item));
    .CreateCriteria("Categories", NHibernate.SqlCommand.JoinType.InnerJoin)
        .Add(Expression.Eq("Customer", c));
criteria.SetFetchMode("Categories", FetchMode.Eager);

return criteria.List();

Однако это не работает, NHibernate по-прежнему выбирает категории с одним выбором для каждого элемента позже.

Я считаю, что NHibernate знает, что результат первого запроса отфильтрован (для клиента), и что категории, возвращаемые запросом, могут быть неполными, следовательно, позже он должен выполнить отдельный запрос для получить категории. (Это предположение верно? Мне кажется разумным, что NHibernate должен работать таким образом, чтобы обеспечить правильные результаты.)

Однако, согласно моим бизнес-правилам (или как вы хотите их называть), элемент не может принадлежать категориям более чем одного клиента, поэтому в действительности я знаю, что результат первого запроса фактически завершен.

У меня вопрос: могу ли я рассказать NHibernate об этом бизнес-правиле каким-либо образом? Есть ли другой способ избежать выбора N + 1 в такой ситуации (что кажется довольно распространенным)?

1 Ответ

2 голосов
/ 23 июня 2009

Постараюсь ответить на мой собственный вопрос, так как у меня пока нет ответа.

Мое решение состоит в том, чтобы разделить проблему на два запроса: сначала получите идентификаторы предметов, принадлежащих данному клиенту:

IQuery query = mySession.CreateQuery("select item.Id from Item as item "
    + "join item.Categories as category "
    + "join category.Customer customer "
    + "where customer.id=:id")
    .SetInt32("id", c.Id);
IList itemIds = query.List();

Затем я получаю фактические товары без каких-либо ограничений для клиента, просто используя идентификаторы. Таким образом, NHibernate знает, что он может получить все категории из одного соединения, избегая выбора N + 1, упомянутого в вопросах:

ICriteria criteria = mySession.CreateCriteria(typeof(MapItem))
    .SetFetchMode("Categories", FetchMode.Eager)
    .SetResultTransformer(new DistinctRootEntityResultTransformer())
    .Add(Expression.In("Id", itemIds));
IList items = criteria.List();

Я не мог придумать ни одного решения, которое сводило бы это к работающему одиночному запросу. Кроме того, этот подход заставляет программиста слишком много знать о внутренней работе NHibernate, и его очень легко пропустить, когда вы пишете новый запрос или критерии. Более общее решение было бы предпочтительным.

...