#join #lazy-loading #linq-to-nhibernate #queryover
#Присоединиться #отложенная загрузка #linq-to-nhibernate #выполнение запроса
Вопрос:
Я пытаюсь создать поисковый запрос с использованием NHibernate, который будет фильтровать параметры из нескольких разных таблиц и приведет к несколько разумному SQL, который может использовать преимущества отложенной загрузки NHibernate. Из различных онлайн-советов, кажется, что самый последний и лучший способ сделать это — использовать объект QueryOver для условного добавления используемых параметров, как в следующем фрагменте:
Hibernate.Criterion.QueryOver<Models.Site, Models.Site> query = NHibernate.Criterion.QueryOver.Of<Models.Site>();
if (!string.IsNullOrEmpty(state))
query = query.WhereRestrictionOn(r => r.State.StateName).IsInsensitiveLike("%" state "%");
if (startDate.HasValue)
query = query.Where(r => r.Events
.Where(e=>e.EventDate >= startDate.Value)
.Count() > 0
);
return query.GetExecutableQueryOver(currentSession).Cacheable().List();
(событие имеет внешний ключ к сайту)
У меня есть два вопроса: Как мне фильтровать дочерние объекты, а не только родительские? Приведенный выше пример кода предоставляет мне все сайты с совпадающими событиями, но внутри этого сайта мне нужны только совпадающие события. Если я должен использовать соединение или подзапрос, каким образом? Я смущен поддержанием моей древовидной иерархии с отложенной загрузкой через объединение или подзапрос.
Редактировать: на этот вопрос был дан ответ. Спасибо, psousa! Как мне добавить предложение or? Я нашел ссылку на объект дизъюнкции, но, похоже, это недоступно с помощью метода QueryOver.
Редактировать: я хочу, чтобы результатом был список сайтов (объект верхнего уровня), отфильтрованный по критериям сайта, и у каждого сайта должен быть свой список событий, отфильтрованных по критериям события.
Я ожидаю, что он сгенерирует SQL следующим образом:
SELECT *
FROM [site] s
LEFT JOIN [event] e ON s.siteID = e.siteID
WHERE e.eventDate > @eventDate
AND (s.stateCd = @state OR s.stateName LIKE @state)
Ответ №1:
Я бы выполнил этот запрос как таковой:
//use aliases. Optional but more practical IMHO
Site siteAlias = null;
Event eventAlias = null;
//use JoinAlias instead of JoinQueryOver to keep the condition at the "Site" level
var results = Session.QueryOver(() => siteAlias)
.JoinAlias(m => m.Event, () => eventAlias)
.Where(() => eventAlias.EventDate > eventDate)
.Where(() => siteAlias.StateCd == state || Restrictions.On(() => siteAlias.StateName).IsLike(state))
.List();
Вы упомянули класс Disjunction, и он фактически может использоваться с QueryOver, например:
var disjunction= new Disjunction();
disjunction.Add(() => siteAlias.StateCD == state);
disjunction.Add(Restrictions.On(() => siteAlias.StateName).IsLike(state));
Запрос при повторном запросе будет:
var results = Session.QueryOver(() => siteAlias)
.JoinAlias(m => m.Event, () => eventAlias)
.Where(() => eventAlias.EventDate > eventDate)
.Where(disjunction)
.List();
Комментарии:
1. Спасибо за вашу помощь! Это не совсем то, что я искал; Я думаю, что мое использование термина Join могло сбить с толку. Я все еще хочу, чтобы NHibernate преобразовывал результаты в древовидную структуру, тогда как это приводит к странному сочетанию структуры объекта и структуры строк, при этом объекты сайта верхнего уровня дублируются событиями, которые к ним прикреплены.
2. В конце концов я выяснил, что для таких объединений, как это, требуется DistinctRootEntityResultTransformer. Еще раз спасибо!
Ответ №2:
При использовании псевдонима соединения, предложенного psousa, вы получите результаты в виде странной комбинации структуры объекта и структуры строк, при этом объекты верхнего уровня дублируются дочерними объектами, которые к ним присоединены. Чтобы получить результаты, которые я искал, вы можете использовать TransformUsing и DistinctRootEntityResultTransformer, как показано в следующем коде:
Site siteAlias = null;
Event eventAlias = null;
var results = currentSession.QueryOver<Site>(() => siteAlias)
.JoinAlias(m => m.Event, () => eventAlias)
.Where(() => eventAlias.EventDate > eventDate)
.Where(() => siteAlias.StateCd == state || Restrictions.On(() => siteAlias.StateName).IsLike(state))
.TransformUsing(new NHibernate.Transform.DistinctRootEntityResultTransformer())
.List();