Skip to content

Commit

Permalink
InMemory: Support for GroupBy aggregate
Browse files Browse the repository at this point in the history
Part of #16963

Resolves #9591
  • Loading branch information
smitpatel committed Sep 3, 2019
1 parent 56d90b7 commit 1746dd0
Show file tree
Hide file tree
Showing 18 changed files with 723 additions and 176 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Reflection;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Storage;
Expand Down Expand Up @@ -161,13 +162,38 @@ private bool TryBindMember(Expression source, MemberIdentity memberIdentity, Typ
return false;
}

private Expression BindProperty(EntityProjectionExpression entityProjectionExpression, IProperty property)
private static Expression BindProperty(EntityProjectionExpression entityProjectionExpression, IProperty property)
{
return entityProjectionExpression.BindProperty(property);
}

private static Expression GetSelector(MethodCallExpression methodCallExpression, GroupByShaperExpression groupByShaperExpression)
{
if (methodCallExpression.Arguments.Count == 1)
{
return groupByShaperExpression.ElementSelector;
}

if (methodCallExpression.Arguments.Count == 2)
{
var selectorLambda = methodCallExpression.Arguments[1].UnwrapLambdaFromQuote();
return ReplacingExpressionVisitor.Replace(
selectorLambda.Parameters[0],
groupByShaperExpression.ElementSelector,
selectorLambda.Body);
}

throw new InvalidOperationException(CoreStrings.TranslationFailed(methodCallExpression.Print()));
}

protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
if (methodCallExpression.Method.IsGenericMethod
&& methodCallExpression.Method.GetGenericMethodDefinition() == EntityMaterializerSource.TryReadValueMethod)
{
return methodCallExpression;
}

// EF.Property case
if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var propertyName))
{
Expand All @@ -179,6 +205,52 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
throw new InvalidOperationException("EF.Property called with wrong property name.");
}

// GroupBy Aggregate case
if (methodCallExpression.Object == null
&& methodCallExpression.Method.DeclaringType == typeof(Enumerable)
&& methodCallExpression.Arguments.Count > 0
&& methodCallExpression.Arguments[0] is InMemoryGroupByShaperExpression groupByShaperExpression)
{
switch (methodCallExpression.Method.Name)
{
case nameof(Enumerable.Average):
case nameof(Enumerable.Max):
case nameof(Enumerable.Min):
case nameof(Enumerable.Sum):
var translation = Translate(GetSelector(methodCallExpression, groupByShaperExpression));
var selector = Expression.Lambda(translation, groupByShaperExpression.ValueBufferParameter);
MethodInfo getMethod()
=> methodCallExpression.Method.Name switch
{
nameof(Enumerable.Average) => InMemoryLinqOperatorProvider.GetAverageWithSelector(selector.ReturnType),
nameof(Enumerable.Max) => InMemoryLinqOperatorProvider.GetMaxWithSelector(selector.ReturnType),
nameof(Enumerable.Min) => InMemoryLinqOperatorProvider.GetMinWithSelector(selector.ReturnType),
nameof(Enumerable.Sum) => InMemoryLinqOperatorProvider.GetSumWithSelector(selector.ReturnType),
_ => throw new InvalidOperationException("Invalid Aggregate Operator encountered."),
};
var method = getMethod();
method = method.GetGenericArguments().Length == 2
? method.MakeGenericMethod(typeof(ValueBuffer), selector.ReturnType)
: method.MakeGenericMethod(typeof(ValueBuffer));

return Expression.Call(method,
groupByShaperExpression.GroupingParameter,
selector);

case nameof(Enumerable.Count):
return Expression.Call(
InMemoryLinqOperatorProvider.CountWithoutPredicate.MakeGenericMethod(typeof(ValueBuffer)),
groupByShaperExpression.GroupingParameter);
case nameof(Enumerable.LongCount):
return Expression.Call(
InMemoryLinqOperatorProvider.LongCountWithoutPredicate.MakeGenericMethod(typeof(ValueBuffer)),
groupByShaperExpression.GroupingParameter);

default:
throw new InvalidOperationException(CoreStrings.TranslationFailed(methodCallExpression.Print()));
}
}

// Subquery case
var subqueryTranslation = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(methodCallExpression);
if (subqueryTranslation != null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query;

namespace Microsoft.EntityFrameworkCore.InMemory.Query.Internal
{
public class InMemoryGroupByShaperExpression : GroupByShaperExpression
{
public InMemoryGroupByShaperExpression(
Expression keySelector,
Expression elementSelector,
ParameterExpression groupingParameter,
ParameterExpression valueBufferParameter)
: base(keySelector, elementSelector)
{
GroupingParameter = groupingParameter;
ValueBufferParameter = valueBufferParameter;
}

public virtual ParameterExpression GroupingParameter { get; }
public virtual ParameterExpression ValueBufferParameter { get; }
}
}
Loading

0 comments on commit 1746dd0

Please sign in to comment.