Skip to content

Commit 5144a57

Browse files
bahusoidfredericDelaporte
authored andcommitted
Allow projections in SelectGroup and CountDistinct expressions (#1987)
Fixes #1985
1 parent 38afb8a commit 5144a57

File tree

5 files changed

+96
-6
lines changed

5 files changed

+96
-6
lines changed

src/NHibernate.Test/Async/Criteria/Lambda/ProjectIntegrationFixture.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ public async Task ProjectTransformToDtoAsync()
9999
var actual = await (s.QueryOver<Person>()
100100
.SelectList(list => list
101101
.SelectGroup(p => p.Name).WithAlias(() => summary.Name)
102+
//GH1985: DateTime.xxxx are not supported in SelectGroup
103+
.SelectGroup(p => p.BirthDate.Year).WithAlias(() => summary.BirthYear)
102104
.Select(Projections.RowCount()).WithAlias(() => summary.Count))
103105
.OrderByAlias(() => summary.Name).Asc
104106
.TransformUsing(Transformers.AliasToBean<PersonSummary>())
@@ -111,5 +113,26 @@ public async Task ProjectTransformToDtoAsync()
111113
Assert.That(actual[1].Count, Is.EqualTo(1));
112114
}
113115
}
116+
117+
[Test]
118+
public async Task ProjecionCountDistinctAsync()
119+
{
120+
if (!TestDialect.SupportsCountDistinct)
121+
Assert.Ignore("Dialect does not support count distinct");
122+
123+
using (var s = OpenSession())
124+
using (s.BeginTransaction())
125+
{
126+
var actual
127+
= (await (s.QueryOver<Person>()
128+
.SelectList(l =>
129+
l.SelectCountDistinct(p => p.BirthDate.Year)
130+
.SelectCountDistinct(p => p.Name))
131+
.ListAsync<object[]>())).FirstOrDefault();
132+
133+
Assert.That((int) actual[0], Is.EqualTo(1), "distinct count by birth year");
134+
Assert.That((int) actual[1], Is.EqualTo(2), "distinct count by name");
135+
}
136+
}
114137
}
115138
}

src/NHibernate.Test/Criteria/Lambda/Model.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public class PersonSummary
9494
{
9595
public string Name { get; set; }
9696
public int Count { get; set; }
97+
public int BirthYear { get; set; }
9798
}
9899

99100
public class Parent

src/NHibernate.Test/Criteria/Lambda/ProjectIntegrationFixture.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ public void ProjectTransformToDto()
8888
var actual = s.QueryOver<Person>()
8989
.SelectList(list => list
9090
.SelectGroup(p => p.Name).WithAlias(() => summary.Name)
91+
//GH1985: DateTime.xxxx are not supported in SelectGroup
92+
.SelectGroup(p => p.BirthDate.Year).WithAlias(() => summary.BirthYear)
9193
.Select(Projections.RowCount()).WithAlias(() => summary.Count))
9294
.OrderByAlias(() => summary.Name).Asc
9395
.TransformUsing(Transformers.AliasToBean<PersonSummary>())
@@ -100,5 +102,26 @@ public void ProjectTransformToDto()
100102
Assert.That(actual[1].Count, Is.EqualTo(1));
101103
}
102104
}
105+
106+
[Test]
107+
public void ProjecionCountDistinct()
108+
{
109+
if (!TestDialect.SupportsCountDistinct)
110+
Assert.Ignore("Dialect does not support count distinct");
111+
112+
using (var s = OpenSession())
113+
using (s.BeginTransaction())
114+
{
115+
var actual
116+
= s.QueryOver<Person>()
117+
.SelectList(l =>
118+
l.SelectCountDistinct(p => p.BirthDate.Year)
119+
.SelectCountDistinct(p => p.Name))
120+
.List<object[]>().FirstOrDefault();
121+
122+
Assert.That((int) actual[0], Is.EqualTo(1), "distinct count by birth year");
123+
Assert.That((int) actual[1], Is.EqualTo(2), "distinct count by name");
124+
}
125+
}
103126
}
104127
}

src/NHibernate/Criterion/Lambda/QueryOverProjectionBuilder.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11

22
using System;
3-
using System.Collections.Generic;
43
using System.Linq.Expressions;
54

65
using NHibernate.Impl;
7-
using NHibernate.SqlCommand;
86

97
namespace NHibernate.Criterion.Lambda
108
{
@@ -50,6 +48,15 @@ public QueryOverProjectionBuilder<T> WithAlias(Expression<Func<object>> alias)
5048
lastProjection = Projections.Alias(lastProjection, aliasContainer);
5149
return this;
5250
}
51+
52+
/// <summary>
53+
/// Create an alias for the previous projection
54+
/// </summary>
55+
public QueryOverProjectionBuilder<T> WithAlias(string alias)
56+
{
57+
lastProjection = Projections.Alias(lastProjection, alias);
58+
return this;
59+
}
5360

5461
/// <summary>
5562
/// Select an arbitrary projection
@@ -119,7 +126,7 @@ public QueryOverProjectionBuilder<T> SelectCountDistinct(Expression<Func<object>
119126
/// </summary>
120127
public QueryOverProjectionBuilder<T> SelectGroup(Expression<Func<T, object>> expression)
121128
{
122-
PushProjection(Projections.Group(expression));
129+
PushProjection(Projections.GroupProjection(expression));
123130
return this;
124131
}
125132

@@ -128,7 +135,7 @@ public QueryOverProjectionBuilder<T> SelectGroup(Expression<Func<T, object>> exp
128135
/// </summary>
129136
public QueryOverProjectionBuilder<T> SelectGroup(Expression<Func<object>> expression)
130137
{
131-
PushProjection(Projections.Group(expression));
138+
PushProjection(Projections.GroupProjection(expression));
132139
return this;
133140
}
134141

src/NHibernate/Criterion/Projections.cs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,16 @@ public static CountProjection Count(string propertyName)
113113
return new CountProjection(propertyName);
114114
}
115115

116+
/// <summary>
117+
/// A distinct projection value count
118+
/// </summary>
119+
/// <param name="projection"></param>
120+
/// <returns></returns>
121+
public static CountProjection CountDistinct(IProjection projection)
122+
{
123+
return Count(projection).SetDistinct();
124+
}
125+
116126
/// <summary>
117127
/// A distinct property value count
118128
/// </summary>
@@ -392,15 +402,15 @@ public static CountProjection Count(Expression<Func<object>> expression)
392402
/// </summary>
393403
public static CountProjection CountDistinct<T>(Expression<Func<T, object>> expression)
394404
{
395-
return Projections.CountDistinct(ExpressionProcessor.FindMemberExpression(expression.Body));
405+
return Count(expression).SetDistinct();
396406
}
397407

398408
/// <summary>
399409
/// A distinct property value count
400410
/// </summary>
401411
public static CountProjection CountDistinct(Expression<Func<object>> expression)
402412
{
403-
return Projections.CountDistinct(ExpressionProcessor.FindMemberExpression(expression.Body));
413+
return Count(expression).SetDistinct();
404414
}
405415

406416
/// <summary>
@@ -410,6 +420,14 @@ public static PropertyProjection Group<T>(Expression<Func<T, object>> expression
410420
{
411421
return Projections.GroupProperty(ExpressionProcessor.FindMemberExpression(expression.Body));
412422
}
423+
424+
/// <summary>
425+
/// A grouping property projection
426+
/// </summary>
427+
public static IProjection GroupProjection<T>(Expression<Func<T, object>> expression)
428+
{
429+
return Create<T, IProjection>(expression, Projections.GroupProperty, Projections.GroupProperty);
430+
}
413431

414432
/// <summary>
415433
/// A grouping property value
@@ -419,6 +437,14 @@ public static PropertyProjection Group(Expression<Func<object>> expression)
419437
return Projections.GroupProperty(ExpressionProcessor.FindMemberExpression(expression.Body));
420438
}
421439

440+
/// <summary>
441+
/// A grouping property projection
442+
/// </summary>
443+
public static IProjection GroupProjection(Expression<Func<object>> expression)
444+
{
445+
return Create<IProjection>(expression, Projections.GroupProperty, Projections.GroupProperty);
446+
}
447+
422448
/// <summary>
423449
/// A property maximum value
424450
/// </summary>
@@ -507,5 +533,15 @@ internal static IProjection ProcessConcat(MethodCallExpression methodCallExpress
507533

508534
return Projections.SqlFunction("concat", NHibernateUtil.String, projections);
509535
}
536+
537+
private static TProjection Create<T, TProjection>(Expression<Func<T, object>> expression, Func<string, TProjection> stringFunc, Func<IProjection, TProjection> projectionFunc)
538+
{
539+
return ExpressionProcessor.FindMemberProjection(expression.Body).Create(stringFunc, projectionFunc);
540+
}
541+
542+
private static TProjection Create<TProjection>(Expression<Func<object>> expression, Func<string, TProjection> stringFunc, Func<IProjection, TProjection> projectionFunc)
543+
{
544+
return ExpressionProcessor.FindMemberProjection(expression.Body).Create(stringFunc, projectionFunc);
545+
}
510546
}
511547
}

0 commit comments

Comments
 (0)