Skip to content

Commit

Permalink
Query: Support for GroupBy entity type
Browse files Browse the repository at this point in the history
Resolves #17653
  • Loading branch information
smitpatel committed Sep 13, 2022
1 parent e178c29 commit 6c07336
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 282 deletions.
16 changes: 16 additions & 0 deletions src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,22 @@ private static Expression GetGroupingKey(Expression key, List<Expression> groupi

return memberInitExpression.Update(updatedNewExpression, memberBindings);

case EntityShaperExpression entityShaperExpression
when entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression:
var entityProjectionExpression = (EntityProjectionExpression)((InMemoryQueryExpression)projectionBindingExpression.QueryExpression)
.GetProjection(projectionBindingExpression);
var readExpressions = new Dictionary<IProperty, MethodCallExpression>();
foreach (var property in GetAllPropertiesInHierarchy(entityProjectionExpression.EntityType))
{
readExpressions[property] = (MethodCallExpression)GetGroupingKey(
entityProjectionExpression.BindProperty(property),
groupingExpressions,
groupingKeyAccessExpression);
}

return entityShaperExpression.Update(
new EntityProjectionExpression(entityProjectionExpression.EntityType, readExpressions));

default:
var index = groupingExpressions.Count;
groupingExpressions.Add(key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,10 @@ private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityTy

return memberInitExpression.Update(updatedNewExpression, newBindings);

case EntityShaperExpression entityShaperExpression
when entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression:
return entityShaperExpression;

default:
var translation = TranslateExpression(expression);
if (translation == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,16 @@ private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType ent
var translatedKey = TranslateGroupingKey(remappedKeySelector);
if (translatedKey == null)
{
return null;
// This could be group by entity type
if (remappedKeySelector is not EntityShaperExpression
{ ValueBufferExpression : ProjectionBindingExpression })
{
// ValueBufferExpression can be JsonQuery, ProjectionBindingExpression, EntityProjection
// We only allow ProjectionBindingExpression which represents a regular entity
return null;
}

translatedKey = remappedKeySelector;
}

if (elementSelector != null)
Expand Down
17 changes: 17 additions & 0 deletions src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2003,6 +2003,23 @@ private static void PopulateGroupByTerms(
PopulateGroupByTerms(unaryExpression.Operand, groupByTerms, groupByAliases, name);
break;

case EntityShaperExpression entityShaperExpression
when entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression:
var entityProjectionExpression = (EntityProjectionExpression)((SelectExpression)projectionBindingExpression.QueryExpression)
.GetProjection(projectionBindingExpression);
foreach (var property in GetAllPropertiesInHierarchy(entityProjectionExpression.EntityType))
{
PopulateGroupByTerms(entityProjectionExpression.BindProperty(property), groupByTerms, groupByAliases, name: null);
}

if (entityProjectionExpression.DiscriminatorExpression != null)
{
PopulateGroupByTerms(
entityProjectionExpression.DiscriminatorExpression, groupByTerms, groupByAliases, name: DiscriminatorColumnAlias);
}

break;

default:
throw new InvalidOperationException(RelationalStrings.InvalidKeySelectorForGroupBy(keySelector, keySelector.GetType()));
}
Expand Down
159 changes: 73 additions & 86 deletions test/EFCore.Specification.Tests/Query/Ef6GroupByTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,18 @@ public virtual Task GroupBy_is_optimized_when_grouping_by_row_and_projecting_col
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Grouping_by_all_columns_doesnt_produce_a_groupby_statement(bool async)
// GroupBy entityType. Issue #17653.
=> AssertTranslationFailed(
() => AssertQuery(
async,
ss => ss.Set<ArubaOwner>().GroupBy(o => o).Select(g => g.Key)));
=> AssertQuery(
async,
ss => ss.Set<ArubaOwner>().GroupBy(o => o).Select(g => g.Key),
elementSorter: e => e.Id,
elementAsserter: (e, a) =>
{
Assert.Equal(e.Id, a.Id);
Assert.Equal(e.Alias, a.Alias);
Assert.Equal(e.FirstName, a.FirstName);
Assert.Equal(e.LastName, a.LastName);
},
entryCount: 10);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
Expand All @@ -132,111 +139,93 @@ public virtual Task Grouping_by_all_columns_with_aggregate_function_works_1(bool
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Grouping_by_all_columns_with_aggregate_function_works_2(bool async)
// GroupBy entityType. Issue #17653.
=> AssertTranslationFailed(
() => AssertQueryScalar(
async,
ss => ss.Set<ArubaOwner>().GroupBy(o => o, c => new { c.LastName, c.FirstName }, (k, g) => g.Count())));
=> AssertQueryScalar(
async,
ss => ss.Set<ArubaOwner>().GroupBy(o => o, c => new { c.LastName, c.FirstName }, (k, g) => g.Count()));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Grouping_by_all_columns_with_aggregate_function_works_3(bool async)
// GroupBy entityType. Issue #17653.
=> AssertTranslationFailed(
() => AssertQueryScalar(
async,
ss => ss.Set<ArubaOwner>().GroupBy(o => o, c => c, (k, g) => g.Count())));
=> AssertQueryScalar(
async,
ss => ss.Set<ArubaOwner>().GroupBy(o => o, c => c, (k, g) => g.Count()));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Grouping_by_all_columns_with_aggregate_function_works_4(bool async)
// GroupBy entityType. Issue #17653.
=> AssertTranslationFailed(
() => AssertQuery(
async,
ss => ss.Set<ArubaOwner>().GroupBy(o => o, c => c, (k, g) => new { Count = g.Count() })));
=> AssertQuery(
async,
ss => ss.Set<ArubaOwner>().GroupBy(o => o, c => c, (k, g) => new { Count = g.Count() }));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Grouping_by_all_columns_with_aggregate_function_works_5(bool async)
// GroupBy entityType. Issue #17653.
=> AssertTranslationFailed(
() => AssertQuery(
async,
ss => ss.Set<ArubaOwner>().GroupBy(o => o, c => c, (k, g) => new { k.Id, Count = g.Count() })));
=> AssertQuery(
async,
ss => ss.Set<ArubaOwner>().GroupBy(o => o, c => c, (k, g) => new { k.Id, Count = g.Count() }));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Grouping_by_all_columns_with_aggregate_function_works_6(bool async)
// GroupBy entityType. Issue #17653.
=> AssertTranslationFailed(
() => AssertQuery(
async,
ss => ss.Set<ArubaOwner>().GroupBy(
o => o, c => c, (k, g) => new
{
k.Id,
k.Alias,
Count = g.Count()
})));
=> AssertQuery(
async,
ss => ss.Set<ArubaOwner>().GroupBy(
o => o, c => c, (k, g) => new
{
k.Id,
k.Alias,
Count = g.Count()
}));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Grouping_by_all_columns_with_aggregate_function_works_7(bool async)
// GroupBy entityType. Issue #17653.
=> AssertTranslationFailed(
() => AssertQueryScalar(
async,
ss => from o in ss.Set<ArubaOwner>()
group o by o
into g
select g.Count()));
=> AssertQueryScalar(
async,
ss => from o in ss.Set<ArubaOwner>()
group o by o
into g
select g.Count());

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Grouping_by_all_columns_with_aggregate_function_works_8(bool async)
// GroupBy entityType. Issue #17653.
=> AssertTranslationFailed(
() => AssertQuery(
async,
ss => from o in ss.Set<ArubaOwner>()
group o by o
into g
select new { g.Key.Id, Count = g.Count() }));
=> AssertQuery(
async,
ss => from o in ss.Set<ArubaOwner>()
group o by o
into g
select new { g.Key.Id, Count = g.Count() });

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Grouping_by_all_columns_with_aggregate_function_works_9(bool async)
// GroupBy entityType. Issue #17653.
=> AssertTranslationFailed(
() => AssertQuery(
async,
ss => from o in ss.Set<ArubaOwner>()
group o by o
into g
select new
{
g.Key.Id,
g.Key.Alias,
Count = g.Count()
}));
=> AssertQuery(
async,
ss => from o in ss.Set<ArubaOwner>()
group o by o
into g
select new
{
g.Key.Id,
g.Key.Alias,
Count = g.Count()
});

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Grouping_by_all_columns_with_aggregate_function_works_10(bool async)
// GroupBy entityType. Issue #17653.
=> AssertTranslationFailed(
() => AssertQuery(
async,
ss => from o in ss.Set<ArubaOwner>()
group o by o
into g
select new
{
g.Key.Id,
Sum = g.Sum(x => x.Id),
Count = g.Count()
}));
=> AssertQuery(
async,
ss => from o in ss.Set<ArubaOwner>()
group o by o
into g
select new
{
g.Key.Id,
Sum = g.Sum(x => x.Id),
Count = g.Count()
});

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
Expand Down Expand Up @@ -736,7 +725,6 @@ public virtual Task Whats_new_2021_sample_13(bool async)
[ConditionalTheory] // From #12088
[MemberData(nameof(IsAsyncData))]
public virtual Task Whats_new_2021_sample_14(bool async)
// GroupBy entityType. Issue #17653.
=> AssertTranslationFailed(
() => AssertQuery(
async,
Expand All @@ -747,13 +735,12 @@ public virtual Task Whats_new_2021_sample_14(bool async)
[ConditionalTheory] // From #12088
[MemberData(nameof(IsAsyncData))]
public virtual Task Whats_new_2021_sample_15(bool async)
// GroupBy entityType. Issue #17653.
=> AssertTranslationFailed(
() => AssertQuery(
async,
ss => ss.Set<Person>()
.GroupBy(bp => bp.Feet)
.Select(g => g.OrderByDescending(bp => bp.Id).FirstOrDefault())));
=> AssertQuery(
async,
ss => ss.Set<Person>()
.GroupBy(bp => bp.Feet)
.Select(g => g.OrderByDescending(bp => bp.Id).FirstOrDefault()),
entryCount: 12);

[ConditionalTheory] // From #12573
[MemberData(nameof(IsAsyncData))]
Expand Down
Loading

0 comments on commit 6c07336

Please sign in to comment.