Хотя ответ Диего является общепринятым методом для выполнения этих задач в 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, которое делает все это автоматически.