Хорошее поведение для быстрой загрузки нескольких братьев и сестер (кузенов?) В NHibernate 3.0 Linq - PullRequest
4 голосов
/ 28 февраля 2011

Я пытаюсь сделать следующее с интерфейсом LINQ NHibernate 3.0.Я хочу запросить объект (с помощью некоторого предложения Where) и загрузить несколько детей и внуков.В настоящее время я делаю это так:

var results = session.Query<Thing>()
                     .Where(...)
                     .Fetch(x => x.SubThingA)
                     .ThenFetch(st => st.SubSubThingA)

                     .Fetch(x => x.SubThingB)
                     .ThenFetch(st => st.SubSubThingB)

                     // etc...

Однако это приводит к декартову произведению между всеми внуками (каждая строка результата содержит много, много столбцов).Это обсуждается "Айенде" здесь .С другой стороны, я получаю одно и то же, в отличие от разбиения запроса и последующего его объединения.

Как я могу сделать это лучше (SQL и с точки зрения производительности), все еще используя интерфейс LINQ от NHibernate?

(С одной стороны, я заметил, что в настоящее время методы ToFuture не работают.не работает при использовании Fetch)

Большое спасибо!

Ответы [ 2 ]

2 голосов
/ 28 февраля 2011

В большинстве случаев вы получите более высокую производительность, используя batch-size в сущностях и коллекциях вместо создания мегазапроса.

В лучшем случае это запрос по идентификатору для корневого типа сущности.


Допустим, у вас есть корневая сущность Клиент , которая имеет коллекцию Заказы , которые имеют коллекцию OrderItems ,какая ссылка Продукты и все свойства batch-size установлены на 1000 .

Допустим, вы получаете список из 10 клиентов, у которых в среднем 10 заказов с 10продукты каждый:

var results = session.Query<Customer>().Where(...).Take(10).ToList();
  • Первый запрос будет выбирать только клиентов.
  • Когда вы начнете выполнять итерацию первой коллекции customer.Orders, один запрос будет использоваться для загрузки всехих ( для всех клиентов )
  • Когда вы начнете выполнять итерацию первого заказа. Коллекция OrderItems будет использоваться для загрузки всех них одним запросом ( для всех заказов и всехклиенты )
  • Когда вы читаетесвойство из первого продукта, один запрос будет использоваться для загрузки всех из них

Итак, у вас есть всего 4 запроса , без каких-либо объединений, получая все по PK,Это просто и эффективно.

1 голос
/ 28 июня 2011

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

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

x => x.Child.GrandChild1
x => x.Child.GrandChild2Collection.SubInclude(c => c.GreatGrandChild)

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

Я анализирую этот списоквыражений и build, для каждого подпути каждого выражения (x, x.Child, x.Child.GrandChild1) запрос критерия NHibernate для корневого типа:

var queryOver = session.QueryOver<T>().Where( ...expression to select root objects... );
for every subpath in the current expression:
    queryOver.RootCriteria.SetFetchMode(subPath, FetchMode.Eager)

queryOver.RootCriteria
         .SetResultTransformer(new DistinctRootEntityResultTransformer())

queryOver.Future()

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

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

Суть в том, что это не простой подвиг.Для меня слишком много кода, чтобы публиковать его как есть.Я предпочитаю API Include (expression) EF4.1, которое делает все это автоматически.

...