NHibernate QueryOver помещает группу в выбор
Это, возможно, задавали несколько раз, и я действительно нашел несколько вопросов (один или два) на ту же тему. Однако эти вопросы были довольно стары, а также тот факт, что они остались без ответа.
По сути, я получил QueryOver (пытался получить тот же результат с Linq для NHibernate, но немного сложнее для Linq):
PrintJobType printJobType = null;
var test = unitOfWork.Session.QueryOver<PrintJob>()
.JoinAlias(pj => pj.PrintJobType, () => printJobType)
.Where(pj => pj.PrintedOn == null)
.Select(Projections.ProjectionList()
.Add(Projections.Group(() => printJobType.PriorityWeight))
.Add(Projections.Group(() => printJobType.ID)))
.OrderBy(pj => printJobType.PriorityWeight).Desc
.OrderBy(Projections.Min<PrintJob>(pj => pj.ID)).Asc
.List<object[]>()
.Select(x => x[1])
.Cast<int>();
Самая большая проблема заключается в том, что я не могу вынести группировку из списка, поскольку мне нужно только вернуть идентификатор типа printjob, но я не могу заставить его работать. Этот запрос в основном сортируется сначала PrintJobWeight и вторым идентификатором Min Printjob.
(Так выглядит текущий запрос:)
SELECT
printjobty1_.PriorityWeight as y0_, printjobty1_.ID as y1_
FROM
[PrintJob] this_
inner join [PrintJobType] printjobty1_ on this_.PrintJobType_id=printjobty1_.ID
WHERE
this_.PrintedOn is null
GROUP BY
printjobty1_.PriorityWeight,
printjobty1_.ID
ORDER BY
printjobty1_.PriorityWeight desc,
min(this_.ID) asc
Поэтому я бы хотел вернуть только идентификатор типа printjob.
В качестве "бонуса" мы действительно хотим выбрать PrintJobType целиком, поскольку в его нынешнем виде я должен сделать вызов.Load(id) сразу после выбора списка, который мне также кажется излишним, но я мог бы с этим смириться. но текущий результат мне кажется просто неэффективным.
То есть в идеальном мире запрос к серверу будет выглядеть так:
SELECT
printjobty1_.*
FROM
[PrintJob] this_
inner join [PrintJobType] printjobty1_ on this_.PrintJobType_id=printjobty1_.ID
WHERE
this_.PrintedOn is null
GROUP BY
printjobty1_.*
ORDER BY
printjobty1_.PriorityWeight desc,
min(this_.ID) asc
(Да, вы не можете поместить printjobty1_.* В предложение group by, но это быстрее, чем записывать все свойства и вещи вручную)
Версия NHibernate: 3.3.1.4000 - SQL Server 2012
1 ответ
Желаемое решение с NHibernate будет использовать подзапрос. Таким образом, мы можем сначала получить нужный нам идентификатор (группировка, сортировка, фильтрация), а затем использовать этот идентификатор для получения четкого результата. Пожалуйста, обратите внимание на силу NHibernate и подзапросов здесь:
Я не уверен на 100%, что вы хотите получить, но есть черновик решения:
PrintJob printJob = null;
PrintJobType printJobType = null;
// simply this is the place, where we do all the magic
// to select the PrintJob.ID
// I guess that my adjustments would result in the same stuff
// as in the query above without the grouping
// ... but if not, we still do have a draft of HOW TO
var subQuery = QueryOver.Of<PrintJob>()
.JoinAlias(pj => pj.PrintJobType, () => printJobType)
.Where(pj => pj.MiddleName == null)
.SelectList(l => l
.Select(pj => pj.ID)
)
.OrderBy(() => printJobType.PriorityWeight).Desc
.OrderBy(pj => pj.ID).Asc
.Take(1)
;
// here we SELECT the clean, root object and its reference...
// that all will be executed as only ONE Select statement
var result = session.QueryOver<PrintJob>(() => printJob)
.WithSubquery
.WhereProperty(() => printJob.ID)
.In(subQuery)
.JoinAlias(x => x.PrintJobType, () => printJobType)
.SingleOrDefault<PrintJob>();
Теперь для некоторых целей тестирования мы можем продолжить:
// here we say session clear just for the TEST purposes below
session.Clear();
// now, session is closed, no lazy loading, but due to JOIN ALIAS
// the PrintJobType is loaded as well
Assert.IsTrue(result != null);
Assert.IsTrue(result.PrintJobType != null);
Assert.IsTrue(result.PrintJobType.PriorityWeight != null);