Стремительная загрузка дерева / иерархий с помощью Nhibernate - PullRequest
6 голосов
/ 07 сентября 2010

Это не вопрос как таковой, я изучал эту тему в течение последних нескольких дней и подумал, что хотел бы поделиться чем-то, что привлекло много разрозненных идей, о которых я читал, и выдвинуло моиРешение проблемы ...

Проблема в том, что в Nhibernate требуется загрузка дочерних объектов n-го уровня, и что nHibernate не будет знать глубину дерева.Я видел, как это решается с помощью прямого sql и Union All, но, к сожалению, я не смог заставить это работать, обычно потому что nhibernate не знает, что вы загружали объекты.Поэтому я рассмотрел следующий код для загрузки объекта с использованием критерия

        var children = Session.CreateCriteria<MenuNode>()
            .SetFetchMode("Children", FetchMode.Eager)
            .Add(Expression.Eq("Id", 1))
            .List<MenuNode>();

. Это приведет к загрузке дочернего элемента для идентификатора 1, здесь вы можете использовать оператор In для загрузки нескольких объектов одновременно.Поэтому мне нужно только обработать все идентификаторы узлов, которые принадлежат этой иерархии, и стремиться загрузить их с помощью оператора In.

Итак, если я создаю родительскую таблицу для иерархии, и у каждого узла есть идентификатор иерархии, я могу получить список всех идентификаторов узлов для этой иерархии, используя эту hql

var sql = "select Distinct id from Nodes where (HierarchyId = :id) ";
var ids = Session.CreateSQLQuery(sql)
          .SetInt32("id", id)
          .List();

иотсюда стремятся загрузить все дочерние элементы для этого дерева в одиночку, используя

var children = Session.CreateCriteria<MenuNode>()
            .SetFetchMode("Children", FetchMode.Eager)
            .Add(Expression.In("Id", ids))
            .List<MenuNode>();

Единственная проблема с этим состоит в том, что вы не пытались загрузить уровни первого уровня узлов из родительской таблицы иерархии, что можно сделать обычным образом..

var menu = Session.CreateCriteria<Menu>()
            .SetFetchMode("RootNodes", FetchMode.Eager)
            .Add(Expression.Eq("Id", id))
            .List<Menu>();

Вот и все, в трех инструкциях SQL вы загрузили дочерний объект на n уровней.Для дальнейшей оптимизации я использовал несколько запросов, чтобы связать два основных запроса и отправить оба оператора одновременно.Оператор для возврата идентификатора необходимо выполнить независимо для возврата идентификатора.

Более подробную информацию об использовании MultiCriteria для быстрой загрузки можно найти здесь ..

http://nhforge.org/blogs/nhibernate/archive/2008/09/06/eager-loading-aggregate-with-many-child-collections.aspx

Надеюсь, это кому-нибудь поможет

1 Ответ

2 голосов
/ 11 апреля 2011

Я понимаю, что вы не задаете вопрос, но я решил поделиться тем, как я предпочитаю это делать. Смотри http://ayende.com/Blog/archive/2009/08/28/nhibernate-tips-amp-tricks-efficiently-selecting-a-tree.aspx

return session.CreateCriteria<MenuNode>()
    .SetFetchMode("Children", FetchMode.Join)
    .SetResultTransformer(new DistinctRootEntityResultTransformer())
    .List<MenuNode>();

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

...