У меня есть приложение, которое использует несколько принципов хранения данных, таких как многомерное моделирование, для создания отчетов по довольно простой базе данных.
Пример (упрощенный) объект с именем Call выглядит следующим образом:
public virtual long Id { get; set; }
public virtual string OriginatorNumber { get; set; }
public virtual string DestinationNumber { get; set; }
public virtual DateDimension DateDimension { get; set; }
Некоторые свойства реальной модели были удалены, поскольку они не имеют значения. Упрощенный DateDimension выглядит так:
public virtual long Id { get; set; }
public virtual DateTime Date { get; set; }
public virtual int DayOfMonth { get; set; }
public virtual int Weekday { get; set; }
Есть еще много таких столбцов - они предварительно заполнены на текущее десятилетие настройкой приложения. Таким образом, каждая дата в течение всего десятилетия имеет строку в этой таблице, и каждый звонок имеет ссылку на дату, когда это произошло. Все это отображается в Fluent NHibernate и работает нормально.
Если я хочу сделать некоторые отчеты, я могу легко сделать это с улучшенным поставщиком NHibernate LINQ в 3.0. Мы хотели бы использовать LINQ для улучшенной управляемости, которую он дает нам, но если мы действительно ДОЛЖНЫ, мы рассмотрим HQL, ICriteria или даже простой SQL.
Итак, скажем, я хочу создать отчет, который показывает количество вызовов с определенного номера, разделенное на день недели, когда они происходят. Я могу легко это сделать так:
var query = Calls
.Where(c => c.OriginatorNumber == "402")
.GroupBy(c => c.DateDimension.Weekday)
.Select(g => new { Day = g.Key, Calls = g.Count() } );
В этом примере «Calls» - это, по сути, IQueryable, возвращаемый поставщиком LINQ (Query) NHibernates через интерфейс репозитория. Приведенный выше запрос дает мне правильные результаты, NHibernate Profiler показывает, что SQL довольно оптимален, все хорошо.
Однако, если я хочу сделать что-то более продвинутое, я застреваю. Скажем, я хочу среднее количество звонков в будний день. Не слишком далеко от вышесказанного, верно? Мне просто нужно выяснить количество уникальных дат каждого дня недели в наборе результатов, поделить общее количество вызовов на него, и мы все настроены - верно? Что ж, нет, именно здесь я начинаю сталкиваться с ограничениями провайдера NHibernate LINQ. С LINQ для объектов я мог бы создать запрос, чтобы сделать это - что-то вроде
.Select(g => g.Count() / g.GroupBy(c => c.DateDimension.Date).Count());
Однако это не приводит к правильному запросу при использовании его в NHibernate. Скорее, он превращает оба вызова .Count () в приведенном выше списке в одинаковое количество (*) записей вызовов, поэтому результат всегда равен 1.
Я МОГУ, конечно, просто запросить каждый вызов, день недели и дату как новый анонимный объект, а затем выполнить математические вычисления на стороне приложения, но, согласно общепринятому мнению, это просто неправильно (тм) Я мог бы в конечном итоге сделать это в отчаянии, даже если это означает боль, когда таблица увеличивается до миллиона вызовов ++.
Ниже приведен SQL-запрос, который дает мне результат, который я ищу.
select ss.Weekday, AVG(cast(ss.Count as decimal))
from
(
select dd.Weekday, dd.Date, COUNT(*) as Count
from Call c
left outer join DateDimension dd
on c.DateDimension_id = dd.Id
where c.OriginatorNumber = '402'
group by dd.Weekday, dd.Date
) ss
group by ss.Weekday
order by ss.Weekday
Возможно ли это сделать с помощью провайдера NHibernate LINQ? Или, если это невозможно, насколько близко я могу подойти, прежде чем позволить приложению получить промежуточный результат, а остальное сделать?