Как написать этот запрос Linq to NHibernate

У нас есть такая сущность:

public class File{
       public int Region{get;set;}
       public bool ShowLocation{get;set;}
       //Other fields are omitted
}

Я хотел бы написать этот запрос:

    SELECT Region,SUM(CASE WHEN ShowLocation=1 THEN 1 ELSE 0 END) AS
 ShowCount,SUM(CASE WHEN ShowLocation=0 THEN 1 ELSE 0 END) AS NotShowCount
--WHERE omitted for the sake of simplicity
GROUP BY Region

По некоторым причинам я хотел бы использовать Linq To Nhibernate (у нас есть сложный механизм фильтрации, который генерирует Expression<Func<File,bool>>)

До сих пор я не мог найти способ добиться этого с помощью Linq To NHibernate. Вот некоторые из моих попыток:

Conditioanl Count: (без исключений, но в любом случае учитываются все строки)

    Files
    .Where(whereExpression)
    .GroupBy(x=>x.Region)
    .Select(x=>new
    {
        x.Region,
        ShowCount=x.Count(f=>f.ShowLocation==1),
        NotShowCount=x.Count(f=>f.ShowLocation==0)
    });

Условная сумма: не поддерживается/реализовано исключение

Files
.Where(whereExpression)
.GroupBy(x=>x.Region)
.Select(x=>new 
{
  x.Region,
  ShowCount=x.SUM(f=>f.ShowLocation==1?1:0),
  NotShowCount=x.SUM(f=>f.ShowLocation==0?1:0)
});

SELECT Перед GROUP: исключение не поддерживается/реализовано

    Files.Where(whereExpression).Select(x=>new
    {
       x.Region,
       Show=x.ShowLocation==1?1:0,
       NotShow=x.ShowLocation==0?1:0
    })
    .GroupBy(x=>x.Region)
    .Select(x=>new 
    {
       x.Region,
       ShowCount=x.SUM(f=>f.Show),
       NotShowCount=x.SUM(f=>f.NotShow)
    });

UNION: исключение не поддерживается/реализовано

    Files
       .Where(whereExpression)
       .Where(x=>x.ShowLocation==1)
 .Select(x=>new
{
x.Region,
Show=1,NotShow=0
})
.Union(Files
 .Where(whereExpression)
.Where(x=>x.ShowLocation==0)
.Select(x=>new
{x.Region,
Show=0,
NotShow=1
}))
.GroupBy(x=>x.Region)
.Select(x=>new 
{
x.Region,
CountShow=x.Count(a=>a.Show),
CountNotShow=x.Count(a=>a.NotShow)
});

У меня нет других зацепок. Любая другая идея?


person Beatles1692    schedule 28.04.2015    source источник


Ответы (2)


Я использовал вашу вторую попытку и вставил ToList() перед Select(). Результат будет выглядеть так:

Files
.Where(whereExpression)
.GroupBy(x=>x.Region)
.ToList<IGrouping<int, File>>()
.Select(x=>new 
{
  x.Key,
  ShowCount = x.Sum(f => f.ShowLocation == 1 ? 1 : 0),
  NotShowCount = x.Sum(f => f.ShowLocation == 0 ? 1 : 0)
});

Таким образом, Select применяется к списку вместо IQueryable.

person Florian Lim    schedule 28.04.2015
comment
Что ж, это может быть альтернативой, если я не могу найти способ создать свой запрос каким-либо другим способом :) спасибо - person Beatles1692; 28.04.2015
comment
Хорошо, я проверил это, и это оказалось очень плохой идеей, потому что это не позволяет вам проецировать единственные столбцы, которые вам нужны, до GroupBy, и, таким образом, он попытается получить весь граф объектов, не загружены ли они лениво, и в нашем случае это приводит к SELECT N+1 :) - person Beatles1692; 28.04.2015

Я не знаю, сможете ли вы заставить это работать с Linq to NH. Сможете ли вы вместо этого использовать QueryOver? API QueryOver также принимает Expression<Func<T,bool>> в предложении where, поэтому вы сможете заставить его работать следующим образом с вашим существующим фильтром:

            MyDto dto = null;

            var myDtoList = session.QueryOver<File>()
                .Select(
                    Projections.Group<File>(x => x.Region).WithAlias(() => dto.Region),
                    Projections.Sum(
                        Projections.Conditional(
                            Restrictions.Where<File>(c => c.ShowLocation== 1),
                            Projections.Constant(1),
                            Projections.Constant(0))).WithAlias(() => dto.ShowCount),
                    Projections.Sum(
                        Projections.Conditional(
                            Restrictions.Where<File>(c => c.ShowLocation== 0),
                            Projections.Constant(1),
                            Projections.Constant(0))).WithAlias(() => dto.NotShowCount))
                .TransformUsing(Transformers.AliasToBean<MyDto>())
                .List<MyDto>();

QueryOver на самом деле не работает с анонимными типами, поэтому вам придется определить MyDto со свойствами, которые вы хотите вернуть, т.е. Region, ShowCount и NotShowCount

person BunkerMentality    schedule 28.04.2015
comment
К сожалению, как я уже упоминал в своем вопросе, я боюсь, что мне придется использовать Linq для NHibernate. - person Beatles1692; 28.04.2015
comment
@ Beatles1692 Хорошо, извините за это, я думал, вы хотите использовать Linq to Nh только потому, что хотите повторно использовать свой механизм фильтрации, который возвращает Expression<Func<File,bool>> - person BunkerMentality; 28.04.2015
comment
Проблема в том, что QueryOver не поддерживает некоторые из Linq expressions, например Any - person Beatles1692; 28.04.2015
comment
Существует способ создания собственных методов расширения для поставщика nhibernate linq. Взгляните на это, возможно: codeproject .com/Articles/318177/ и, возможно, попробуйте создать собственное расширение, например, SumWhere(x=›x.ShowLocation==1) или что-то в этом роде. Я никогда не пробовал, но может стоит попробовать? - person BunkerMentality; 28.04.2015