Search code examples
t-sqlnhibernatelinq-to-nhibernatenhibernate-3

Conditional row count in linq to nhibernate doesn't work


I want to translate following simple sql query into Linq to NHibernate:

SELECT NewsId 
     ,sum(n.UserHits) as 'HitsNumber'
     ,sum(CASE WHEN n.UserHits > 0 THEN 1 ELSE 0 END) as 'VisitorsNumber'
FROM UserNews n
GROUP BY n.NewsId

My simplified UserNews class:

public class AktualnosciUzytkownik
{
    public virtual int UserNewsId { get; set; }
    public virtual int UserHits { get; set; }        
    public virtual User User { get; set; }  // UserId key in db table
    public virtual News News { get; set; }  // NewsId key in db table
}

I've written following linq query:

var hitsPerNews = (from n in Session.Query<UserNews>() 
                   group n by n.News.NewsId into g
                   select new { NewsId = g.Key, HitsNumber = g.Sum(x => x.UserHits), 
                   VisitorsNumber = g.Count(x => x.UserHits > 0) }).ToList();

But generated sql just ignores my x => x.UserHits > 0 statement and makes unnecessary 'left outer join':

SELECT   news1_.NewsId               AS col_0_0_,
         CAST(SUM(news0_.UserHits) AS INT) AS col_1_0_,
         CAST(COUNT(*) AS             INT) AS col_2_0_
FROM     UserNews news0_
         LEFT OUTER JOIN News news1_
         ON       news0_.NewsId=news1_.NewsId
GROUP BY news1_.NewsId

How Can I fix or workaround this issue? Maybe this can be done better with QueryOver syntax?


Solution

  • I finally found answer to my question, my solution is based on an answer to this question:

    My QueryOver code (I still don't know how to do it in Linq to NHibernate):

    UserHitsDto adDtoAlias = null;
    
    var userHits = Session.QueryOver<UserNews>()
        .Select(Projections.Group<UserNews>(c => c.News.NewsId)
                                       .WithAlias(() => adDtoAlias.NewsId),
                Projections.Sum<UserNews>(x => x.UserHits)
                                       .WithAlias(() => adDtoAlias.HitsNumber),
                Projections.Sum(Projections.Conditional(
                    Restrictions.Where<UserNews>(f => f.UserHits > 0),
                    Projections.Constant(1),
                    Projections.Constant(0)
                )).WithAlias(() => adDtoAlias.VisitorsNumber)
               )
        .TransformUsing(Transformers.AliasToBean<UserHitsDto>())
        .List<UserHitsDto>();
    

    It produces following tsql:

    SELECT   this_.NewsId  AS y0_,
             SUM(this_.UserHits) AS y1_,
             SUM((
             CASE
                      WHEN this_.UserHits > @p0
                      THEN @p1
                      ELSE @p2
             END)) AS y2_
    FROM     UserNews this_
    GROUP BY this_.NewsId
    

    where @p0 = 0, @p1 = 1, @p2 = 0