From d68c9fb6be3e3092cf617b03fa7609373c069360 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Mon, 1 Jul 2019 11:58:25 -0700 Subject: [PATCH] Query: Translate to SQL GROUP BY when aggregate operator is applied after GroupBy Resolves #12826 Resolves #6658 Part of #15711 Resolves #15853 Resolves #12799 Resolves #12476 Resolves #11976 There are way too many existing issues are resolved by this PR. I haven't added regression test or verified each of them so I have put Verify-Fixed label on them for now. --- .../Query/Pipeline/QuerySqlGenerator.cs | 14 + ...ionalProjectionBindingExpressionVisitor.cs | 1 - ...yableMethodTranslatingExpressionVisitor.cs | 107 ++++++- ...ShapedQueryOptimizingExpressionVisitors.cs | 5 +- ...lationalSqlTranslatingExpressionVisitor.cs | 91 ++++-- .../Query/Pipeline/SqlExpressionFactory.cs | 1 + .../SqlExpressions/SelectExpression.cs | 222 +++++++++++--- ...rchConditionConvertingExpressionVisitor.cs | 15 +- .../Query/Pipeline/GroupByShaperExpression.cs | 51 ++++ .../Pipeline/ReplacingExpressionVisitor.cs | 6 + .../InMemoryComplianceTest.cs | 1 + .../Query/GroupByQueryInMemoryTest.cs | 2 +- .../Query/GearsOfWarQueryTestBase.cs | 8 +- .../Query/GroupByQueryTestBase.cs | 279 +++++++++--------- .../Query/GroupByQuerySqlServerTest.cs | 193 ++++++++---- .../Query/IncludeSqlServerTest.cs | 14 +- .../Query/QueryBugsTest.cs | 32 +- 17 files changed, 741 insertions(+), 301 deletions(-) create mode 100644 src/EFCore/Query/Pipeline/GroupByShaperExpression.cs diff --git a/src/EFCore.Relational/Query/Pipeline/QuerySqlGenerator.cs b/src/EFCore.Relational/Query/Pipeline/QuerySqlGenerator.cs index 5d8597b7616..e1b10b5419e 100644 --- a/src/EFCore.Relational/Query/Pipeline/QuerySqlGenerator.cs +++ b/src/EFCore.Relational/Query/Pipeline/QuerySqlGenerator.cs @@ -167,6 +167,20 @@ protected virtual void GenerateSelect(SelectExpression selectExpression) Visit(selectExpression.Predicate); } + if (selectExpression.GroupBy.Count > 0) + { + _relationalCommandBuilder.AppendLine().Append("GROUP BY "); + + GenerateList(selectExpression.GroupBy, e => Visit(e)); + } + + if (selectExpression.HavingExpression != null) + { + _relationalCommandBuilder.AppendLine().Append("HAVING "); + + Visit(selectExpression.HavingExpression); + } + GenerateOrderings(selectExpression); GenerateLimitOffset(selectExpression); } diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalProjectionBindingExpressionVisitor.cs b/src/EFCore.Relational/Query/Pipeline/RelationalProjectionBindingExpressionVisitor.cs index 19c65788b39..afaa6e83959 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalProjectionBindingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalProjectionBindingExpressionVisitor.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; -using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.NavigationExpansion; using Microsoft.EntityFrameworkCore.Query.Pipeline; diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/Pipeline/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 37ef8aeb8f0..03fd2c62569 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -266,7 +266,105 @@ protected override ShapedQueryExpression TranslateFirstOrDefault(ShapedQueryExpr return source; } - protected override ShapedQueryExpression TranslateGroupBy(ShapedQueryExpression source, LambdaExpression keySelector, LambdaExpression elementSelector, LambdaExpression resultSelector) => throw new NotImplementedException(); + protected override ShapedQueryExpression TranslateGroupBy( + ShapedQueryExpression source, + LambdaExpression keySelector, + LambdaExpression elementSelector, + LambdaExpression resultSelector) + { + var selectExpression = (SelectExpression)source.QueryExpression; + selectExpression.PrepareForAggregate(); + + var remappedKeySelector = RemapLambdaBody(source.ShaperExpression, keySelector); + + var translatedKey = TranslateGroupingKey(remappedKeySelector) + ?? (remappedKeySelector is ConstantExpression ? remappedKeySelector : null); + if (translatedKey != null) + { + if (elementSelector != null) + { + source = TranslateSelect(source, elementSelector); + } + + var sqlKeySelector = translatedKey is ConstantExpression + ? _sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.Constant(1)) + : translatedKey; + + var appliedKeySelector = selectExpression.ApplyGrouping(sqlKeySelector); + translatedKey = translatedKey is ConstantExpression ? translatedKey : appliedKeySelector; + + source.ShaperExpression = new GroupByShaperExpression(translatedKey, source.ShaperExpression); + + if (resultSelector == null) + { + return source; + } + + var keyAccessExpression = Expression.MakeMemberAccess( + source.ShaperExpression, + source.ShaperExpression.Type.GetTypeInfo().GetMember(nameof(IGrouping.Key))[0]); + + var newResultSelectorBody = ReplacingExpressionVisitor.Replace( + resultSelector.Parameters[0], keyAccessExpression, + resultSelector.Parameters[1], source.ShaperExpression, + resultSelector.Body); + + source.ShaperExpression = _projectionBindingExpressionVisitor.Translate(selectExpression, newResultSelectorBody); + + return source; + } + + throw new InvalidOperationException(); + } + + private Expression TranslateGroupingKey(Expression expression) + { + if (expression is NewExpression newExpression) + { + if (newExpression.Arguments.Count == 0) + { + return newExpression; + } + + var newArguments = new Expression[newExpression.Arguments.Count]; + for (var i = 0; i < newArguments.Length; i++) + { + newArguments[i] = TranslateGroupingKey(newExpression.Arguments[i]); + if (newArguments[i] == null) + { + return null; + } + } + + return newExpression.Update(newArguments); + } + + if (expression is MemberInitExpression memberInitExpression) + { + var updatedNewExpression = (NewExpression)TranslateGroupingKey(memberInitExpression.NewExpression); + if (updatedNewExpression == null) + { + return null; + } + + var newBindings = new MemberAssignment[memberInitExpression.Bindings.Count]; + for (var i = 0; i < newBindings.Length; i++) + { + var memberAssignment = (MemberAssignment)memberInitExpression.Bindings[i]; + var visitedExpression = TranslateGroupingKey(memberAssignment.Expression); + if (visitedExpression == null) + { + return null; + } + + newBindings[i] = memberAssignment.Update(visitedExpression); + } + + return memberInitExpression.Update(updatedNewExpression, newBindings); + } + + return _sqlTranslator.Translate(expression); + } protected override ShapedQueryExpression TranslateGroupJoin(ShapedQueryExpression outer, ShapedQueryExpression inner, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector) { @@ -589,9 +687,7 @@ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression s } var newSelectorBody = ReplacingExpressionVisitor.Replace(selector.Parameters.Single(), source.ShaperExpression, selector.Body); - - source.ShaperExpression = _projectionBindingExpressionVisitor - .Translate(selectExpression, newSelectorBody); + source.ShaperExpression = _projectionBindingExpressionVisitor.Translate(selectExpression, newSelectorBody); return source; } @@ -599,7 +695,8 @@ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression s private static readonly MethodInfo _defaultIfEmptyWithoutArgMethodInfo = typeof(Enumerable).GetTypeInfo() .GetDeclaredMethods(nameof(Enumerable.DefaultIfEmpty)).Single(mi => mi.GetParameters().Length == 1); - protected override ShapedQueryExpression TranslateSelectMany(ShapedQueryExpression source, LambdaExpression collectionSelector, LambdaExpression resultSelector) + protected override ShapedQueryExpression TranslateSelectMany( + ShapedQueryExpression source, LambdaExpression collectionSelector, LambdaExpression resultSelector) { var collectionSelectorBody = collectionSelector.Body; //var defaultIfEmpty = false; diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryOptimizingExpressionVisitors.cs b/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryOptimizingExpressionVisitors.cs index 44aae76545c..d1df34bf6b9 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryOptimizingExpressionVisitors.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryOptimizingExpressionVisitors.cs @@ -51,7 +51,10 @@ protected override Expression VisitExtension(Expression extensionExpression) var collectionId = _collectionId++; var selectExpression = (SelectExpression)collectionShaperExpression.Projection.QueryExpression; // Do pushdown beforehand so it updates all pending collections first - if (selectExpression.IsDistinct || selectExpression.Limit != null || selectExpression.Offset != null) + if (selectExpression.IsDistinct + || selectExpression.Limit != null + || selectExpression.Offset != null + || selectExpression.GroupBy.Count > 1) { selectExpression.PushdownIntoSubquery(); } diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/Pipeline/RelationalSqlTranslatingExpressionVisitor.cs index 6bd20241c7d..7b6fbcb4d22 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalSqlTranslatingExpressionVisitor.cs @@ -56,6 +56,13 @@ public virtual SqlExpression Translate(Expression expression) translation = _sqlExpressionFactory.ApplyDefaultTypeMapping(translation); + if (translation is SqlConstantExpression + && translation.TypeMapping == null) + { + // Non-mappable constant + return null; + } + _sqlVerifyingExpressionVisitor.Visit(translation); return translation; @@ -246,13 +253,66 @@ protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExp return null; } + private 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(); + } + protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) { + // EF.Property case if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var propertyName)) { return BindProperty(source, propertyName); } + // GroupBy Aggregate case + if (methodCallExpression.Object == null + && methodCallExpression.Method.DeclaringType == typeof(Enumerable) + && methodCallExpression.Arguments.Count > 0 + && methodCallExpression.Arguments[0] is GroupByShaperExpression groupByShaperExpression) + { + switch (methodCallExpression.Method.Name) + { + case nameof(Enumerable.Average): + return TranslateAverage(GetSelector(methodCallExpression, groupByShaperExpression)); + + case nameof(Enumerable.Count): + return TranslateCount(); + + case nameof(Enumerable.LongCount): + return TranslateLongCount(); + + case nameof(Enumerable.Max): + return TranslateMax(GetSelector(methodCallExpression, groupByShaperExpression)); + + case nameof(Enumerable.Min): + return TranslateMin(GetSelector(methodCallExpression, groupByShaperExpression)); + + case nameof(Enumerable.Sum): + return TranslateSum(GetSelector(methodCallExpression, groupByShaperExpression)); + + default: + throw new InvalidOperationException("Unknown aggregate operator encountered."); + } + + } + + // Subquery case var subqueryTranslation = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(methodCallExpression); if (subqueryTranslation != null) { @@ -280,6 +340,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp return new SubSelectExpression(subquery); } + // MethodCall translators var @object = Visit(methodCallExpression.Object); if (TranslationFailed(methodCallExpression.Object, @object)) { @@ -413,29 +474,25 @@ protected override Expression VisitParameter(ParameterExpression parameterExpres protected override Expression VisitExtension(Expression extensionExpression) { - if (extensionExpression is EntityShaperExpression) + switch (extensionExpression) { - return extensionExpression; - } + case EntityShaperExpression _: + case SqlExpression _: + return extensionExpression; - if (extensionExpression is ProjectionBindingExpression projectionBindingExpression) - { - var selectExpression = (SelectExpression)projectionBindingExpression.QueryExpression; + case NullConditionalExpression nullConditionalExpression: + return Visit(nullConditionalExpression.AccessOperation); - return selectExpression.GetMappedProjection(projectionBindingExpression.ProjectionMember); - } + case CorrelationPredicateExpression correlationPredicateExpression: + return Visit(correlationPredicateExpression.EqualExpression); - if (extensionExpression is NullConditionalExpression nullConditionalExpression) - { - return Visit(nullConditionalExpression.AccessOperation); - } + case ProjectionBindingExpression projectionBindingExpression: + var selectExpression = (SelectExpression)projectionBindingExpression.QueryExpression; + return selectExpression.GetMappedProjection(projectionBindingExpression.ProjectionMember); - if (extensionExpression is CorrelationPredicateExpression correlationPredicateExpression) - { - return Visit(correlationPredicateExpression.EqualExpression); + default: + return null; } - - return base.VisitExtension(extensionExpression); } protected override Expression VisitConditional(ConditionalExpression conditionalExpression) diff --git a/src/EFCore.Relational/Query/Pipeline/SqlExpressionFactory.cs b/src/EFCore.Relational/Query/Pipeline/SqlExpressionFactory.cs index 60b65babcbd..265c600c727 100644 --- a/src/EFCore.Relational/Query/Pipeline/SqlExpressionFactory.cs +++ b/src/EFCore.Relational/Query/Pipeline/SqlExpressionFactory.cs @@ -505,6 +505,7 @@ public SelectExpression Select(SqlExpression projection) alias: null, new List(), new List(), + new List(), new List()); if (projection != null) diff --git a/src/EFCore.Relational/Query/Pipeline/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/Pipeline/SqlExpressions/SelectExpression.cs index e170d8531a0..22ac29e1a00 100644 --- a/src/EFCore.Relational/Query/Pipeline/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/Pipeline/SqlExpressions/SelectExpression.cs @@ -22,6 +22,7 @@ private readonly IDictionary>(); private readonly List _tables = new List(); + private readonly List _groupBy = new List(); private readonly List _orderings = new List(); private readonly List _identifier = new List(); @@ -30,9 +31,11 @@ private readonly IDictionary Projection => _projection; public IReadOnlyList Tables => _tables; + public IReadOnlyList GroupBy => _groupBy; public IReadOnlyList Orderings => _orderings; public ISet Tags { get; private set; } = new HashSet(); public SqlExpression Predicate { get; private set; } + public SqlExpression HavingExpression { get; private set; } public SqlExpression Limit { get; private set; } public SqlExpression Offset { get; private set; } public bool IsDistinct { get; private set; } @@ -57,11 +60,13 @@ internal SelectExpression( string alias, List projections, List tables, + List groupBy, List orderings) : base(alias) { _projection = projections; _tables = tables; + _groupBy = groupBy; _orderings = orderings; } @@ -87,7 +92,7 @@ internal SelectExpression(IEntityType entityType) } } - public SelectExpression(IEntityType entityType, string sql, Expression arguments) + internal SelectExpression(IEntityType entityType, string sql, Expression arguments) : base(null) { var fromSqlExpression = new FromSqlExpression( @@ -115,6 +120,8 @@ public bool IsNonComposedFromSql() && Offset == null && !IsDistinct && Predicate == null + && GroupBy.Count == 0 + && HavingExpression == null && Orderings.Count == 0 && Tables.Count == 1 && Tables[0] is FromSqlExpression fromSql @@ -223,7 +230,7 @@ public IDictionary AddToProjection(EntityProjectionExpression en public void PrepareForAggregate() { - if (IsDistinct || Limit != null || Offset != null || IsSetOperation) + if (IsDistinct || Limit != null || Offset != null || IsSetOperation || GroupBy.Count > 0) { PushdownIntoSubquery(); } @@ -243,23 +250,89 @@ public void ApplyPredicate(SqlExpression expression) expression = new SqlRemappingVisitor(mappings).Remap(expression); } - if (Predicate == null) + if (_groupBy.Count > 0) { - Predicate = expression; + if (HavingExpression == null) + { + HavingExpression = expression; + } + else + { + HavingExpression = new SqlBinaryExpression( + ExpressionType.AndAlso, + HavingExpression, + expression, + typeof(bool), + expression.TypeMapping); + } } else { - Predicate = new SqlBinaryExpression( - ExpressionType.AndAlso, - Predicate, - expression, - typeof(bool), - expression.TypeMapping); + if (Predicate == null) + { + Predicate = expression; + } + else + { + Predicate = new SqlBinaryExpression( + ExpressionType.AndAlso, + Predicate, + expression, + typeof(bool), + expression.TypeMapping); + } } } public override ExpressionType NodeType => ExpressionType.Extension; + public Expression ApplyGrouping(Expression keySelector) + { + ClearOrdering(); + + if (keySelector is SqlConstantExpression + || keySelector is SqlParameterExpression) + { + PushdownIntoSubquery(); + var subquery = (SelectExpression)_tables[0]; + var projectionIndex = subquery.AddToProjection((SqlExpression)keySelector, nameof(IGrouping.Key)); + + keySelector = new ColumnExpression(subquery.Projection[projectionIndex], subquery, false); + } + + AppendGroupBy(keySelector); + + return keySelector; + } + + private void AppendGroupBy(Expression keySelector) + { + switch (keySelector) + { + case SqlExpression sqlExpression: + _groupBy.Add(sqlExpression); + break; + + case NewExpression newExpression: + foreach (var argument in newExpression.Arguments) + { + AppendGroupBy(argument); + } + break; + + case MemberInitExpression memberInitExpression: + AppendGroupBy(memberInitExpression.NewExpression); + foreach (var argument in memberInitExpression.Bindings) + { + AppendGroupBy(((MemberAssignment)argument).Expression); + } + break; + + default: + throw new InvalidOperationException("Invalid keySelector for Group By"); + } + } + public void ApplyOrdering(OrderingExpression orderingExpression) { @@ -342,7 +415,6 @@ public void ClearOrdering() _orderings.Clear(); } - /// /// Applies a set operation (e.g. Union, Intersect) on this query, pushing it down and /// down to be the set operands. @@ -360,10 +432,12 @@ public Expression ApplySetOperation( Expression shaperExpression) { // TODO: throw if there are pending collection joins - var select1 = new SelectExpression(null, new List(), _tables.ToList(), _orderings.ToList()) + // TODO: What happens when applying set operations on 2 queries with one of them being grouping + var select1 = new SelectExpression(null, new List(), _tables.ToList(), _groupBy.ToList(), _orderings.ToList()) { IsDistinct = IsDistinct, Predicate = Predicate, + HavingExpression = HavingExpression, Offset = Offset, Limit = Limit, SetOperationType = SetOperationType @@ -420,6 +494,7 @@ public Expression ApplySetOperation( Limit = null; IsDistinct = false; Predicate = null; + HavingExpression = null; _orderings.Clear(); _tables.Clear(); _tables.Add(select1); @@ -433,23 +508,23 @@ void HandleEntityMapping( SelectExpression select1, EntityProjectionExpression projection1, SelectExpression select2, EntityProjectionExpression projection2) { - var propertyExpressions = new Dictionary(); + var propertyExpressions = new Dictionary(); - if (projection1.EntityType == projection2.EntityType) - { - foreach (var property in GetAllPropertiesInHierarchy(projection1.EntityType)) - { - propertyExpressions[property] = AddSetOperationColumnProjections( - property, - select1, projection1.GetProperty(property), - select2, projection2.GetProperty(property)); - } + if (projection1.EntityType == projection2.EntityType) + { + foreach (var property in GetAllPropertiesInHierarchy(projection1.EntityType)) + { + propertyExpressions[property] = AddSetOperationColumnProjections( + property, + select1, projection1.GetProperty(property), + select2, projection2.GetProperty(property)); + } - _projectionMapping[projectionMember] = new EntityProjectionExpression(projection1.EntityType, propertyExpressions); - return; - } + _projectionMapping[projectionMember] = new EntityProjectionExpression(projection1.EntityType, propertyExpressions); + return; + } - throw new InvalidOperationException("Set operations over different entity types are currently unsupported (see #16298)"); + throw new InvalidOperationException("Set operations over different entity types are currently unsupported (see #16298)"); } ColumnExpression AddSetOperationColumnProjections( @@ -486,10 +561,11 @@ void HandleColumnMapping( public IDictionary PushdownIntoSubquery() { - var subquery = new SelectExpression("t", new List(), _tables.ToList(), _orderings.ToList()) + var subquery = new SelectExpression("t", new List(), _tables.ToList(), _groupBy.ToList(), _orderings.ToList()) { IsDistinct = IsDistinct, Predicate = Predicate, + HavingExpression = HavingExpression, Offset = Offset, Limit = Limit, SetOperationType = SetOperationType @@ -554,7 +630,7 @@ public IDictionary PushdownIntoSubquery() { _identifier.Add(outerColumn); } - else if (!IsDistinct) + else if (!IsDistinct && GroupBy.Count == 0) { var index = subquery.AddToProjection(identifier); var projectionExpression = subquery._projection[index]; @@ -572,7 +648,7 @@ public IDictionary PushdownIntoSubquery() { _childIdentifiers.Add(outerColumn); } - else if (!IsDistinct) + else if (!IsDistinct && GroupBy.Count == 0) { var index = subquery.AddToProjection(identifier); var projectionExpression = subquery._projection[index]; @@ -602,9 +678,11 @@ public IDictionary PushdownIntoSubquery() Limit = null; IsDistinct = false; Predicate = null; + HavingExpression = null; SetOperationType = SetOperationType.None; _tables.Clear(); _tables.Add(subquery); + _groupBy.Clear(); return projectionMap; } @@ -652,7 +730,8 @@ public Expression ApplyCollectionJoin( || innerSelectExpression.Limit != null || innerSelectExpression.IsDistinct || innerSelectExpression.Predicate != null - || innerSelectExpression.Tables.Count > 1) + || innerSelectExpression.Tables.Count > 1 + || innerSelectExpression.GroupBy.Count > 1) { var orderings = innerSelectExpression.Orderings.ToList(); var sqlRemappingVisitor = new SqlRemappingVisitor(innerSelectExpression.PushdownIntoSubquery()); @@ -849,6 +928,12 @@ private bool ContainsTableReference(TableExpressionBase table) public void AddInnerJoin(SelectExpression innerSelectExpression, SqlExpression joinPredicate, Type transparentIdentifierType) { + if (Limit != null || Offset != null || IsDistinct || IsSetOperation || GroupBy.Count > 1) + { + joinPredicate = new SqlRemappingVisitor(PushdownIntoSubquery()) + .Remap(joinPredicate); + } + // TODO: write a test which has distinct on outer so that we can verify pushdown if (innerSelectExpression.Orderings.Any() || innerSelectExpression.Limit != null @@ -856,7 +941,8 @@ public void AddInnerJoin(SelectExpression innerSelectExpression, SqlExpression j || innerSelectExpression.IsDistinct // TODO: Predicate can be lifted in inner join || innerSelectExpression.Predicate != null - || innerSelectExpression.Tables.Count > 1) + || innerSelectExpression.Tables.Count > 1 + || innerSelectExpression.GroupBy.Count > 1) { joinPredicate = new SqlRemappingVisitor(innerSelectExpression.PushdownIntoSubquery()) .Remap(joinPredicate); @@ -884,7 +970,7 @@ public void AddInnerJoin(SelectExpression innerSelectExpression, SqlExpression j public void AddLeftJoin(SelectExpression innerSelectExpression, SqlExpression joinPredicate, Type transparentIdentifierType) { - if (Limit != null || Offset != null || IsDistinct || IsSetOperation) + if (Limit != null || Offset != null || IsDistinct || IsSetOperation || GroupBy.Count > 1) { joinPredicate = new SqlRemappingVisitor(PushdownIntoSubquery()) .Remap(joinPredicate); @@ -895,7 +981,8 @@ public void AddLeftJoin(SelectExpression innerSelectExpression, SqlExpression jo || innerSelectExpression.Offset != null || innerSelectExpression.IsDistinct || innerSelectExpression.Predicate != null - || innerSelectExpression.Tables.Count > 1) + || innerSelectExpression.Tables.Count > 1 + || innerSelectExpression.GroupBy.Count > 1) { joinPredicate = new SqlRemappingVisitor(innerSelectExpression.PushdownIntoSubquery()) .Remap(joinPredicate); @@ -932,7 +1019,7 @@ public void AddLeftJoin(SelectExpression innerSelectExpression, SqlExpression jo public void AddCrossJoin(SelectExpression innerSelectExpression, Type transparentIdentifierType) { - if (Limit != null || Offset != null || IsDistinct || Predicate != null || IsSetOperation) + if (Limit != null || Offset != null || IsDistinct || Predicate != null || IsSetOperation || GroupBy.Count > 1) { PushdownIntoSubquery(); } @@ -941,7 +1028,9 @@ public void AddCrossJoin(SelectExpression innerSelectExpression, Type transparen || innerSelectExpression.Limit != null || innerSelectExpression.Offset != null || innerSelectExpression.IsDistinct - || innerSelectExpression.Predicate != null) + || innerSelectExpression.Predicate != null + || innerSelectExpression.Tables.Count > 1 + || innerSelectExpression.GroupBy.Count > 1) { innerSelectExpression.PushdownIntoSubquery(); } @@ -1021,6 +1110,12 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) Predicate = (SqlExpression)visitor.Visit(Predicate); + var groupBy = _groupBy.ToList(); + _groupBy.Clear(); + _groupBy.AddRange(GroupBy.Select(e => (SqlExpression)visitor.Visit(e))); + + HavingExpression = (SqlExpression)visitor.Visit(HavingExpression); + var orderings = _orderings.ToList(); _orderings.Clear(); _orderings.AddRange(orderings.Select(e => e.Update((SqlExpression)visitor.Visit(e.Expression)))); @@ -1070,6 +1165,17 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) var predicate = (SqlExpression)visitor.Visit(Predicate); changed |= predicate != Predicate; + var groupBy = new List(); + foreach (var groupingKey in _groupBy) + { + var newGroupingKey = (SqlExpression)visitor.Visit(groupingKey); + changed |= newGroupingKey != groupingKey; + groupBy.Add(newGroupingKey); + } + + var havingExpression = (SqlExpression)visitor.Visit(HavingExpression); + changed |= havingExpression != HavingExpression; + var orderings = new List(); foreach (var ordering in _orderings) { @@ -1086,10 +1192,11 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) if (changed) { - var newSelectExpression = new SelectExpression(Alias, projections, tables, orderings) + var newSelectExpression = new SelectExpression(Alias, projections, tables, groupBy, orderings) { _projectionMapping = projectionMapping, Predicate = predicate, + HavingExpression = havingExpression, Offset = offset, Limit = limit, IsDistinct = IsDistinct, @@ -1158,6 +1265,17 @@ private bool Equals(SelectExpression selectExpression) return false; } + if (!_groupBy.SequenceEqual(selectExpression._groupBy)) + { + return false; + } + + if (!(HavingExpression == null && selectExpression.HavingExpression == null + || HavingExpression != null && Predicate.Equals(selectExpression.HavingExpression))) + { + return false; + } + if (!_orderings.SequenceEqual(selectExpression._orderings)) { return false; @@ -1183,6 +1301,8 @@ public SelectExpression Update( List projections, List tables, SqlExpression predicate, + List groupBy, + SqlExpression havingExpression, List orderings, SqlExpression limit, SqlExpression offset, @@ -1195,10 +1315,11 @@ public SelectExpression Update( projectionMapping[kvp.Key] = kvp.Value; } - return new SelectExpression(alias, projections, tables, orderings) + return new SelectExpression(alias, projections, tables, groupBy, orderings) { _projectionMapping = projectionMapping, Predicate = predicate, + HavingExpression = havingExpression, Offset = offset, Limit = limit, IsDistinct = distinct, @@ -1226,6 +1347,13 @@ public override int GetHashCode() hash.Add(Predicate); + foreach (var groupingKey in _groupBy) + { + hash.Add(groupingKey); + } + + hash.Add(HavingExpression); + foreach (var ordering in _orderings) { hash.Add(ordering); @@ -1305,16 +1433,24 @@ public override void Print(ExpressionPrinter expressionPrinter) expressionPrinter.StringBuilder.AppendLine().Append("WHERE "); expressionPrinter.Visit(Predicate); } + + if (GroupBy.Any()) + { + expressionPrinter.StringBuilder.AppendLine().Append("GROUP BY "); + expressionPrinter.VisitList(GroupBy); + } + + if (HavingExpression != null) + { + expressionPrinter.StringBuilder.AppendLine().Append("HAVING "); + expressionPrinter.Visit(HavingExpression); + } } if (Orderings.Any()) { - var orderings = Orderings.ToList(); - if (orderings.Count > 0) - { - expressionPrinter.StringBuilder.AppendLine().Append("ORDER BY "); - expressionPrinter.VisitList(orderings); - } + expressionPrinter.StringBuilder.AppendLine().Append("ORDER BY "); + expressionPrinter.VisitList(Orderings); } else if (Offset != null) { diff --git a/src/EFCore.SqlServer/Query/Pipeline/SearchConditionConvertingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Pipeline/SearchConditionConvertingExpressionVisitor.cs index 21b1badb401..42006d9a172 100644 --- a/src/EFCore.SqlServer/Query/Pipeline/SearchConditionConvertingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Pipeline/SearchConditionConvertingExpressionVisitor.cs @@ -146,6 +146,19 @@ protected override Expression VisitSelect(SelectExpression selectExpression) var predicate = (SqlExpression)Visit(selectExpression.Predicate); changed |= predicate != selectExpression.Predicate; + var groupBy = new List(); + _isSearchCondition = false; + foreach (var groupingKey in selectExpression.GroupBy) + { + var newGroupingKey = (SqlExpression)Visit(groupingKey); + changed |= newGroupingKey != groupingKey; + groupBy.Add(newGroupingKey); + } + + _isSearchCondition = true; + var havingExpression = (SqlExpression)Visit(selectExpression.HavingExpression); + changed |= havingExpression != selectExpression.HavingExpression; + var orderings = new List(); _isSearchCondition = false; foreach (var ordering in selectExpression.Orderings) @@ -166,7 +179,7 @@ protected override Expression VisitSelect(SelectExpression selectExpression) if (changed) { return selectExpression.Update( - projections, tables, predicate, orderings, limit, offset, selectExpression.IsDistinct, selectExpression.Alias); + projections, tables, predicate, groupBy, havingExpression, orderings, limit, offset, selectExpression.IsDistinct, selectExpression.Alias); } return selectExpression; diff --git a/src/EFCore/Query/Pipeline/GroupByShaperExpression.cs b/src/EFCore/Query/Pipeline/GroupByShaperExpression.cs new file mode 100644 index 00000000000..85e7f87eb22 --- /dev/null +++ b/src/EFCore/Query/Pipeline/GroupByShaperExpression.cs @@ -0,0 +1,51 @@ +// 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; +using System.Linq; +using System.Linq.Expressions; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Query.Expressions.Internal; +using Microsoft.EntityFrameworkCore.Query.Internal; + +namespace Microsoft.EntityFrameworkCore.Query.Pipeline +{ + public class GroupByShaperExpression : Expression, IPrintable + { + public GroupByShaperExpression(Expression keySelector, Expression elementSelector) + { + KeySelector = keySelector; + ElementSelector = elementSelector; + } + + public Expression KeySelector { get; } + public Expression ElementSelector { get; } + + public override Type Type => typeof(IGrouping<,>).MakeGenericType(KeySelector.Type, ElementSelector.Type); + public override ExpressionType NodeType => ExpressionType.Extension; + + public void Print(ExpressionPrinter expressionPrinter) + { + expressionPrinter.StringBuilder.AppendLine("GroupBy("); + expressionPrinter.StringBuilder.Append("KeySelector: "); + expressionPrinter.Visit(KeySelector); + expressionPrinter.StringBuilder.AppendLine(", "); + expressionPrinter.StringBuilder.Append("ElementSelector:"); + expressionPrinter.Visit(ElementSelector); + expressionPrinter.StringBuilder.AppendLine(")"); + } + + protected override Expression VisitChildren(ExpressionVisitor visitor) + { + var keySelector = visitor.Visit(KeySelector); + var elementSelector = visitor.Visit(ElementSelector); + + return Update(keySelector, elementSelector); + } + + public GroupByShaperExpression Update(Expression keySelector, Expression elementSelector) + => keySelector != KeySelector || elementSelector != ElementSelector + ? new GroupByShaperExpression(keySelector, elementSelector) + : this; + } +} diff --git a/src/EFCore/Query/Pipeline/ReplacingExpressionVisitor.cs b/src/EFCore/Query/Pipeline/ReplacingExpressionVisitor.cs index fb97cc58183..784d2a200e9 100644 --- a/src/EFCore/Query/Pipeline/ReplacingExpressionVisitor.cs +++ b/src/EFCore/Query/Pipeline/ReplacingExpressionVisitor.cs @@ -52,6 +52,12 @@ protected override Expression VisitMember(MemberExpression memberExpression) { var innerExpression = Visit(memberExpression.Expression); + if (innerExpression is GroupByShaperExpression groupByShaperExpression + && memberExpression.Member.Name == nameof(IGrouping.Key)) + { + return groupByShaperExpression.KeySelector; + } + if (innerExpression is NewExpression newExpression) { var index = newExpression.Members.IndexOf(memberExpression.Member); diff --git a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs index e6c4919e0bc..caeaff18d23 100644 --- a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs @@ -24,6 +24,7 @@ public class InMemoryComplianceTest : ComplianceTestBase typeof(OwnedQueryTestBase<>), // issue #15285 // Query pipeline typeof(SimpleQueryTestBase<>), + typeof(GroupByQueryTestBase<>), typeof(ConcurrencyDetectorTestBase<>), typeof(AsNoTrackingTestBase<>), typeof(AsTrackingTestBase<>), diff --git a/test/EFCore.InMemory.FunctionalTests/Query/GroupByQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/GroupByQueryInMemoryTest.cs index 98ffa0cb2e6..666d6df2635 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/GroupByQueryInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/GroupByQueryInMemoryTest.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - public class GroupByQueryInMemoryTest : GroupByQueryTestBase> + internal class GroupByQueryInMemoryTest : GroupByQueryTestBase> { public GroupByQueryInMemoryTest( NorthwindQueryInMemoryFixture fixture, diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs index 765e07a30b4..aa2d5e03d11 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs @@ -3658,7 +3658,7 @@ public virtual Task Can_order_by_indexed_property_on_query(bool isAsync) assertOrder: true); } - [ConditionalTheory(Skip = "Issue #15249")] + [ConditionalTheory(Skip = "Issue #15799")] [MemberData(nameof(IsAsyncData))] public virtual Task Can_group_by_indexed_property_on_query(bool isAsync) { @@ -3667,7 +3667,7 @@ public virtual Task Can_group_by_indexed_property_on_query(bool isAsync) cs => cs.GroupBy(c => c[City.NationPropertyName]).Select(g => g.Count())); } - [ConditionalTheory(Skip = "Issue #15249")] + [ConditionalTheory(Skip = "Issue #15799")] [MemberData(nameof(IsAsyncData))] public virtual Task Can_group_by_converted_indexed_property_on_query(bool isAsync) { @@ -7041,7 +7041,7 @@ public virtual Task GroupBy_Property_Include_Select_Count(bool isAsync) gs => gs.Include(g => g.CityOfBirth).GroupBy(g => g.Rank).Select(g => g.Count())); } - [ConditionalTheory(Skip = "issue #150249")] + [ConditionalTheory(Skip = "issue #15249")] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Include_Select_LongCount(bool isAsync) { @@ -7050,7 +7050,7 @@ public virtual Task GroupBy_Property_Include_Select_LongCount(bool isAsync) gs => gs.Include(g => g.CityOfBirth).GroupBy(g => g.Rank).Select(g => g.LongCount())); } - [ConditionalTheory(Skip = "issue #150249")] + [ConditionalTheory(Skip = "issue #15249")] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Include_Select_Max(bool isAsync) { diff --git a/test/EFCore.Specification.Tests/Query/GroupByQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GroupByQueryTestBase.cs index 03e65f25431..75b9eb4dea6 100644 --- a/test/EFCore.Specification.Tests/Query/GroupByQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GroupByQueryTestBase.cs @@ -29,7 +29,7 @@ protected virtual void ClearLog() #region GroupByProperty - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Average(bool isAsync) { @@ -47,7 +47,7 @@ public virtual Task GroupBy_Property_Select_Average_with_navigation_expansion(bo os => os.Where(o => o.Customer.City != "London").GroupBy(o => o.CustomerID, (k, es) => new { k, es }).Select(g => g.es.Average(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Count(bool isAsync) { @@ -56,7 +56,7 @@ public virtual Task GroupBy_Property_Select_Count(bool isAsync) os => os.GroupBy(o => o.CustomerID).Select(g => g.Count())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_LongCount(bool isAsync) { @@ -65,7 +65,7 @@ public virtual Task GroupBy_Property_Select_LongCount(bool isAsync) os => os.GroupBy(o => o.CustomerID).Select(g => g.LongCount())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Max(bool isAsync) { @@ -74,7 +74,7 @@ public virtual Task GroupBy_Property_Select_Max(bool isAsync) os => os.GroupBy(o => o.CustomerID).Select(g => g.Max(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Min(bool isAsync) { @@ -83,7 +83,7 @@ public virtual Task GroupBy_Property_Select_Min(bool isAsync) os => os.GroupBy(o => o.CustomerID).Select(g => g.Min(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Sum(bool isAsync) { @@ -93,7 +93,7 @@ public virtual Task GroupBy_Property_Select_Sum(bool isAsync) o => EF.Property(o, "CustomerID")).Select(g => g.Sum(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Sum_Min_Max_Avg(bool isAsync) { @@ -111,7 +111,7 @@ public virtual Task GroupBy_Property_Select_Sum_Min_Max_Avg(bool isAsync) e => e.Min + " " + e.Max); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Key_Average(bool isAsync) { @@ -127,7 +127,7 @@ public virtual Task GroupBy_Property_Select_Key_Average(bool isAsync) e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Key_Count(bool isAsync) { @@ -143,7 +143,7 @@ public virtual Task GroupBy_Property_Select_Key_Count(bool isAsync) e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Key_LongCount(bool isAsync) { @@ -159,7 +159,7 @@ public virtual Task GroupBy_Property_Select_Key_LongCount(bool isAsync) e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Key_Max(bool isAsync) { @@ -175,7 +175,7 @@ public virtual Task GroupBy_Property_Select_Key_Max(bool isAsync) e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Key_Min(bool isAsync) { @@ -191,7 +191,7 @@ public virtual Task GroupBy_Property_Select_Key_Min(bool isAsync) e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Key_Sum(bool isAsync) { @@ -207,7 +207,7 @@ public virtual Task GroupBy_Property_Select_Key_Sum(bool isAsync) e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Key_Sum_Min_Max_Avg(bool isAsync) { @@ -226,7 +226,7 @@ public virtual Task GroupBy_Property_Select_Key_Sum_Min_Max_Avg(bool isAsync) e => e.Min + " " + e.Max); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Sum_Min_Key_Max_Avg(bool isAsync) { @@ -245,7 +245,7 @@ public virtual Task GroupBy_Property_Select_Sum_Min_Key_Max_Avg(bool isAsync) e => e.Min + " " + e.Max); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_key_multiple_times_and_aggregate(bool isAsync) { @@ -262,8 +262,7 @@ public virtual Task GroupBy_Property_Select_key_multiple_times_and_aggregate(boo e => e.Key1); } - // also #15249 - [ConditionalTheory(Skip = "issue #12826")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_aggregate_projecting_conditional_expression_based_on_group_key(bool isAsync) { @@ -283,7 +282,7 @@ public virtual Task GroupBy_aggregate_projecting_conditional_expression_based_on #region GroupByAnonymousAggregate - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_anonymous_Select_Average(bool isAsync) { @@ -296,7 +295,7 @@ public virtual Task GroupBy_anonymous_Select_Average(bool isAsync) }).Select(g => g.Average(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_anonymous_Select_Count(bool isAsync) { @@ -309,7 +308,7 @@ public virtual Task GroupBy_anonymous_Select_Count(bool isAsync) }).Select(g => g.Count())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_anonymous_Select_LongCount(bool isAsync) { @@ -322,7 +321,7 @@ public virtual Task GroupBy_anonymous_Select_LongCount(bool isAsync) }).Select(g => g.LongCount())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_anonymous_Select_Max(bool isAsync) { @@ -335,7 +334,7 @@ public virtual Task GroupBy_anonymous_Select_Max(bool isAsync) }).Select(g => g.Max(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_anonymous_Select_Min(bool isAsync) { @@ -348,7 +347,7 @@ public virtual Task GroupBy_anonymous_Select_Min(bool isAsync) }).Select(g => g.Min(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_anonymous_Select_Sum(bool isAsync) { @@ -361,7 +360,7 @@ public virtual Task GroupBy_anonymous_Select_Sum(bool isAsync) }).Select(g => g.Sum(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_anonymous_Select_Sum_Min_Max_Avg(bool isAsync) { @@ -383,7 +382,7 @@ public virtual Task GroupBy_anonymous_Select_Sum_Min_Max_Avg(bool isAsync) e => e.Min + " " + e.Max); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_anonymous_with_alias_Select_Key_Sum(bool isAsync) { @@ -402,7 +401,7 @@ public virtual Task GroupBy_anonymous_with_alias_Select_Key_Sum(bool isAsync) })); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Average(bool isAsync) { @@ -416,7 +415,7 @@ public virtual Task GroupBy_Composite_Select_Average(bool isAsync) }).Select(g => g.Average(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Count(bool isAsync) { @@ -430,7 +429,7 @@ public virtual Task GroupBy_Composite_Select_Count(bool isAsync) }).Select(g => g.Count())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_LongCount(bool isAsync) { @@ -444,7 +443,7 @@ public virtual Task GroupBy_Composite_Select_LongCount(bool isAsync) }).Select(g => g.LongCount())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Max(bool isAsync) { @@ -458,7 +457,7 @@ public virtual Task GroupBy_Composite_Select_Max(bool isAsync) }).Select(g => g.Max(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Min(bool isAsync) { @@ -472,7 +471,7 @@ public virtual Task GroupBy_Composite_Select_Min(bool isAsync) }).Select(g => g.Min(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Sum(bool isAsync) { @@ -486,7 +485,7 @@ public virtual Task GroupBy_Composite_Select_Sum(bool isAsync) }).Select(g => g.Sum(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Sum_Min_Max_Avg(bool isAsync) { @@ -509,7 +508,7 @@ public virtual Task GroupBy_Composite_Select_Sum_Min_Max_Avg(bool isAsync) e => e.Min + " " + e.Max); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Key_Average(bool isAsync) { @@ -530,7 +529,7 @@ public virtual Task GroupBy_Composite_Select_Key_Average(bool isAsync) e => e.Key.CustomerID + " " + e.Key.EmployeeID); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Key_Count(bool isAsync) { @@ -551,7 +550,7 @@ public virtual Task GroupBy_Composite_Select_Key_Count(bool isAsync) e => e.Key.CustomerID + " " + e.Key.EmployeeID); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Key_LongCount(bool isAsync) { @@ -572,7 +571,7 @@ public virtual Task GroupBy_Composite_Select_Key_LongCount(bool isAsync) e => e.Key.CustomerID + " " + e.Key.EmployeeID); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Key_Max(bool isAsync) { @@ -593,7 +592,7 @@ public virtual Task GroupBy_Composite_Select_Key_Max(bool isAsync) e => e.Key.CustomerID + " " + e.Key.EmployeeID); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Key_Min(bool isAsync) { @@ -614,7 +613,7 @@ public virtual Task GroupBy_Composite_Select_Key_Min(bool isAsync) e => e.Key.CustomerID + " " + e.Key.EmployeeID); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Key_Sum(bool isAsync) { @@ -635,7 +634,7 @@ public virtual Task GroupBy_Composite_Select_Key_Sum(bool isAsync) e => e.Key.CustomerID + " " + e.Key.EmployeeID); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Key_Sum_Min_Max_Avg(bool isAsync) { @@ -659,7 +658,7 @@ public virtual Task GroupBy_Composite_Select_Key_Sum_Min_Max_Avg(bool isAsync) e => e.Min + " " + e.Max); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Sum_Min_Key_Max_Avg(bool isAsync) { @@ -683,7 +682,7 @@ public virtual Task GroupBy_Composite_Select_Sum_Min_Key_Max_Avg(bool isAsync) e => e.Min + " " + e.Max); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Sum_Min_Key_flattened_Max_Avg(bool isAsync) { @@ -708,8 +707,7 @@ public virtual Task GroupBy_Composite_Select_Sum_Min_Key_flattened_Max_Avg(bool e => e.Min + " " + e.Max); } - // also #15249 - [ConditionalTheory(Skip = "Issue #14935. Cannot eval 'GroupBy(new NominalType() {CustomerID = [o].CustomerID, EmployeeID = [o].EmployeeID}, [o])'")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Dto_as_key_Select_Sum(bool isAsync) { @@ -729,8 +727,7 @@ public virtual Task GroupBy_Dto_as_key_Select_Sum(bool isAsync) })); } - // also #15249 - [ConditionalTheory(Skip = "Issue #14935. Cannot eval 'GroupBy([o].CustomerID, new NominalType() {CustomerID = [o].CustomerID, EmployeeID = [o].EmployeeID})'")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Dto_as_element_selector_Select_Sum(bool isAsync) { @@ -771,7 +768,7 @@ private bool Equals(NominalType other) && EmployeeID == other.EmployeeID; } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Dto_Sum_Min_Key_flattened_Max_Avg(bool isAsync) { @@ -826,7 +823,7 @@ private bool Equals(CompositeDto other) && string.Equals(CustomerId, other.CustomerId); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Composite_Select_Sum_Min_part_Key_flattened_Max_Avg(bool isAsync) { @@ -850,7 +847,7 @@ public virtual Task GroupBy_Composite_Select_Sum_Min_part_Key_flattened_Max_Avg( e => e.Min + " " + e.Max); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Constant_Select_Sum_Min_Key_Max_Avg(bool isAsync) { @@ -869,7 +866,7 @@ public virtual Task GroupBy_Constant_Select_Sum_Min_Key_Max_Avg(bool isAsync) e => e.Min + " " + e.Max); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Constant_with_element_selector_Select_Sum(bool isAsync) { @@ -884,7 +881,7 @@ public virtual Task GroupBy_Constant_with_element_selector_Select_Sum(bool isAsy e => e.Sum); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Constant_with_element_selector_Select_Sum2(bool isAsync) { @@ -899,7 +896,7 @@ public virtual Task GroupBy_Constant_with_element_selector_Select_Sum2(bool isAs e => e.Sum); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Constant_with_element_selector_Select_Sum3(bool isAsync) { @@ -914,7 +911,7 @@ public virtual Task GroupBy_Constant_with_element_selector_Select_Sum3(bool isAs e => e.Sum); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_after_predicate_Constant_Select_Sum_Min_Key_Max_Avg(bool isAsync) { @@ -933,7 +930,7 @@ public virtual Task GroupBy_after_predicate_Constant_Select_Sum_Min_Key_Max_Avg( e => e.Min + " " + e.Max); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Constant_with_element_selector_Select_Sum_Min_Key_Max_Avg(bool isAsync) { @@ -949,7 +946,7 @@ public virtual Task GroupBy_Constant_with_element_selector_Select_Sum_Min_Key_Ma e => e.Sum); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_param_Select_Sum_Min_Key_Max_Avg(bool isAsync) { @@ -970,7 +967,7 @@ public virtual Task GroupBy_param_Select_Sum_Min_Key_Max_Avg(bool isAsync) e => e.Min + " " + e.Max); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_param_with_element_selector_Select_Sum(bool isAsync) { @@ -987,7 +984,7 @@ public virtual Task GroupBy_param_with_element_selector_Select_Sum(bool isAsync) e => e.Sum); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_param_with_element_selector_Select_Sum2(bool isAsync) { @@ -1004,7 +1001,7 @@ public virtual Task GroupBy_param_with_element_selector_Select_Sum2(bool isAsync e => e.Sum); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_param_with_element_selector_Select_Sum3(bool isAsync) { @@ -1021,7 +1018,7 @@ public virtual Task GroupBy_param_with_element_selector_Select_Sum3(bool isAsync e => e.Sum); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_param_with_element_selector_Select_Sum_Min_Key_Max_Avg(bool isAsync) { @@ -1043,7 +1040,7 @@ public virtual Task GroupBy_param_with_element_selector_Select_Sum_Min_Key_Max_A #region GroupByWithElementSelectorAggregate - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_scalar_element_selector_Average(bool isAsync) { @@ -1052,7 +1049,7 @@ public virtual Task GroupBy_Property_scalar_element_selector_Average(bool isAsyn os => os.GroupBy(o => o.CustomerID, o => o.OrderID).Select(g => g.Average())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_scalar_element_selector_Count(bool isAsync) { @@ -1061,7 +1058,7 @@ public virtual Task GroupBy_Property_scalar_element_selector_Count(bool isAsync) os => os.GroupBy(o => o.CustomerID, o => o.OrderID).Select(g => g.Count())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_scalar_element_selector_LongCount(bool isAsync) { @@ -1070,7 +1067,7 @@ public virtual Task GroupBy_Property_scalar_element_selector_LongCount(bool isAs os => os.GroupBy(o => o.CustomerID, o => o.OrderID).Select(g => g.LongCount())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_scalar_element_selector_Max(bool isAsync) { @@ -1079,7 +1076,7 @@ public virtual Task GroupBy_Property_scalar_element_selector_Max(bool isAsync) os => os.GroupBy(o => o.CustomerID, o => o.OrderID).Select(g => g.Max())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_scalar_element_selector_Min(bool isAsync) { @@ -1088,7 +1085,7 @@ public virtual Task GroupBy_Property_scalar_element_selector_Min(bool isAsync) os => os.GroupBy(o => o.CustomerID, o => o.OrderID).Select(g => g.Min())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_scalar_element_selector_Sum(bool isAsync) { @@ -1097,7 +1094,7 @@ public virtual Task GroupBy_Property_scalar_element_selector_Sum(bool isAsync) os => os.GroupBy(o => o.CustomerID, o => o.OrderID).Select(g => g.Sum())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_scalar_element_selector_Sum_Min_Max_Avg(bool isAsync) { @@ -1115,7 +1112,7 @@ public virtual Task GroupBy_Property_scalar_element_selector_Sum_Min_Max_Avg(boo e => e.Min + " " + e.Max); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_anonymous_element_selector_Average(bool isAsync) { @@ -1129,7 +1126,7 @@ public virtual Task GroupBy_Property_anonymous_element_selector_Average(bool isA }).Select(g => g.Average(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_anonymous_element_selector_Count(bool isAsync) { @@ -1143,7 +1140,7 @@ public virtual Task GroupBy_Property_anonymous_element_selector_Count(bool isAsy }).Select(g => g.Count())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_anonymous_element_selector_LongCount(bool isAsync) { @@ -1157,7 +1154,7 @@ public virtual Task GroupBy_Property_anonymous_element_selector_LongCount(bool i }).Select(g => g.LongCount())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_anonymous_element_selector_Max(bool isAsync) { @@ -1171,7 +1168,7 @@ public virtual Task GroupBy_Property_anonymous_element_selector_Max(bool isAsync }).Select(g => g.Max(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_anonymous_element_selector_Min(bool isAsync) { @@ -1185,7 +1182,7 @@ public virtual Task GroupBy_Property_anonymous_element_selector_Min(bool isAsync }).Select(g => g.Min(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_anonymous_element_selector_Sum(bool isAsync) { @@ -1199,7 +1196,7 @@ public virtual Task GroupBy_Property_anonymous_element_selector_Sum(bool isAsync }).Select(g => g.Sum(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_anonymous_element_selector_Sum_Min_Max_Avg(bool isAsync) { @@ -1222,7 +1219,7 @@ public virtual Task GroupBy_Property_anonymous_element_selector_Sum_Min_Max_Avg( e => e.Sum + " " + e.Avg); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_element_selector_complex_aggregate(bool isAsync) { @@ -1232,7 +1229,7 @@ public virtual Task GroupBy_element_selector_complex_aggregate(bool isAsync) .Select(g => g.Sum(e => e.OrderID + 1))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_element_selector_complex_aggregate2(bool isAsync) { @@ -1242,8 +1239,7 @@ public virtual Task GroupBy_element_selector_complex_aggregate2(bool isAsync) .Select(g => g.Sum(e => e.OrderID + 1))); } - // issue #15249 - [ConditionalTheory(Skip = "Issue #14935. Cannot eval 'GroupBy([o].CustomerID, [o].OrderID)' could not be translated and will be evaluated locally.'")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_element_selector_complex_aggregate3(bool isAsync) { @@ -1253,7 +1249,7 @@ public virtual Task GroupBy_element_selector_complex_aggregate3(bool isAsync) .Select(g => g.Sum(e => e + 1))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_element_selector_complex_aggregate4(bool isAsync) { @@ -1267,7 +1263,7 @@ public virtual Task GroupBy_element_selector_complex_aggregate4(bool isAsync) #region GroupByAfterComposition - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_empty_key_Aggregate(bool isAsync) { @@ -1281,7 +1277,7 @@ public virtual Task GroupBy_empty_key_Aggregate(bool isAsync) .Select(g => g.Sum(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_empty_key_Aggregate_Key(bool isAsync) { @@ -1300,7 +1296,7 @@ public virtual Task GroupBy_empty_key_Aggregate_Key(bool isAsync) })); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task OrderBy_GroupBy_Aggregate(bool isAsync) { @@ -1312,7 +1308,7 @@ public virtual Task OrderBy_GroupBy_Aggregate(bool isAsync) .Select(g => g.Sum(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task OrderBy_Skip_GroupBy_Aggregate(bool isAsync) { @@ -1325,7 +1321,7 @@ public virtual Task OrderBy_Skip_GroupBy_Aggregate(bool isAsync) .Select(g => g.Average(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task OrderBy_Take_GroupBy_Aggregate(bool isAsync) { @@ -1338,7 +1334,7 @@ public virtual Task OrderBy_Take_GroupBy_Aggregate(bool isAsync) .Select(g => g.Min(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task OrderBy_Skip_Take_GroupBy_Aggregate(bool isAsync) { @@ -1352,7 +1348,7 @@ public virtual Task OrderBy_Skip_Take_GroupBy_Aggregate(bool isAsync) .Select(g => g.Max(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Distinct_GroupBy_Aggregate(bool isAsync) { @@ -1370,7 +1366,7 @@ public virtual Task Distinct_GroupBy_Aggregate(bool isAsync) e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Anonymous_projection_Distinct_GroupBy_Aggregate(bool isAsync) { @@ -1394,7 +1390,7 @@ public virtual Task Anonymous_projection_Distinct_GroupBy_Aggregate(bool isAsync e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#15711")] [MemberData(nameof(IsAsyncData))] public virtual Task SelectMany_GroupBy_Aggregate(bool isAsync) { @@ -1412,7 +1408,7 @@ public virtual Task SelectMany_GroupBy_Aggregate(bool isAsync) e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Join_GroupBy_Aggregate(bool isAsync) { @@ -1432,7 +1428,7 @@ on o.CustomerID equals c.CustomerID e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#15249")] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_required_navigation_member_Aggregate(bool isAsync) { @@ -1450,7 +1446,7 @@ public virtual Task GroupBy_required_navigation_member_Aggregate(bool isAsync) e => e.CustomerId); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Join_complex_GroupBy_Aggregate(bool isAsync) { @@ -1471,7 +1467,7 @@ on o.CustomerID equals c.CustomerID e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupJoin_GroupBy_Aggregate(bool isAsync) { @@ -1494,7 +1490,7 @@ from o in grouping.DefaultIfEmpty() e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupJoin_GroupBy_Aggregate_2(bool isAsync) { @@ -1516,7 +1512,7 @@ from o in grouping.DefaultIfEmpty() e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupJoin_GroupBy_Aggregate_3(bool isAsync) { @@ -1538,7 +1534,7 @@ from c in grouping.DefaultIfEmpty() e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupJoin_GroupBy_Aggregate_4(bool isAsync) { @@ -1560,7 +1556,7 @@ from o in grouping.DefaultIfEmpty() e => e.Value); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupJoin_GroupBy_Aggregate_5(bool isAsync) { @@ -1582,7 +1578,7 @@ from c in grouping.DefaultIfEmpty() e => e.Value); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#15249")] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_optional_navigation_member_Aggregate(bool isAsync) { @@ -1600,7 +1596,7 @@ public virtual Task GroupBy_optional_navigation_member_Aggregate(bool isAsync) e => e.Country); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupJoin_complex_GroupBy_Aggregate(bool isAsync) { @@ -1624,7 +1620,7 @@ where o.OrderID > 10300 e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Self_join_GroupBy_Aggregate(bool isAsync) { @@ -1644,7 +1640,7 @@ on o1.OrderID equals o2.OrderID e => e.Key); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#15249")] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_multi_navigation_members_Aggregate(bool isAsync) { @@ -1667,8 +1663,7 @@ public virtual Task GroupBy_multi_navigation_members_Aggregate(bool isAsync) e => e.CompositeKey.CustomerID + " " + e.CompositeKey.ProductName); } - // also #15249 - [ConditionalTheory(Skip = "Unable to bind group by. See Issue#6658")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Union_simple_groupby(bool isAsync) { @@ -1682,11 +1677,10 @@ public virtual Task Union_simple_groupby(bool isAsync) { g.Key, Total = g.Count() - }), - entryCount: 19); + })); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Select_anonymous_GroupBy_Aggregate(bool isAsync) { @@ -1711,7 +1705,7 @@ public virtual Task Select_anonymous_GroupBy_Aggregate(bool isAsync) })); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#15249")] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_principal_key_property_optimization(bool isAsync) { @@ -1730,7 +1724,7 @@ public virtual Task GroupBy_principal_key_property_optimization(bool isAsync) #region GroupByAggregateComposition - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_OrderBy_key(bool isAsync) { @@ -1748,7 +1742,7 @@ public virtual Task GroupBy_OrderBy_key(bool isAsync) assertOrder: true); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_OrderBy_count(bool isAsync) { @@ -1767,7 +1761,7 @@ public virtual Task GroupBy_OrderBy_count(bool isAsync) assertOrder: true); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_OrderBy_count_Select_sum(bool isAsync) { @@ -1786,7 +1780,7 @@ public virtual Task GroupBy_OrderBy_count_Select_sum(bool isAsync) assertOrder: true); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_aggregate_Contains(bool isAsync) { @@ -1800,7 +1794,7 @@ public virtual Task GroupBy_aggregate_Contains(bool isAsync) entryCount: 31); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_aggregate_Pushdown(bool isAsync) { @@ -1814,9 +1808,8 @@ public virtual Task GroupBy_aggregate_Pushdown(bool isAsync) .Skip(4)); } - // issue #12577 - //[ConditionalTheory] - //[MemberData(nameof(IsAsyncData))] + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_aggregate_Pushdown_followed_by_projecting_Length(bool isAsync) { return AssertQueryScalar( @@ -1830,9 +1823,8 @@ public virtual Task GroupBy_aggregate_Pushdown_followed_by_projecting_Length(boo .Select(e => e.Length)); } - // issue #12600 - //[ConditionalTheory] - //[MemberData(nameof(IsAsyncData))] + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_aggregate_Pushdown_followed_by_projecting_constant(bool isAsync) { return AssertQueryScalar( @@ -1843,7 +1835,7 @@ public virtual Task GroupBy_aggregate_Pushdown_followed_by_projecting_constant(b .OrderBy(t => t) .Take(20) .Skip(4) - .Select(e => 1)); + .Select(e => 5)); } [ConditionalFact(Skip = "Issue #14935. Cannot eval 'GroupBy([o].CustomerID, [o])'")] @@ -1866,7 +1858,7 @@ public virtual void GroupBy_Select_sum_over_unmapped_property() } } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_filter_key(bool isAsync) { @@ -1883,7 +1875,7 @@ public virtual Task GroupBy_filter_key(bool isAsync) })); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_filter_count(bool isAsync) { @@ -1900,7 +1892,7 @@ public virtual Task GroupBy_filter_count(bool isAsync) })); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_filter_count_OrderBy_count_Select_sum(bool isAsync) { @@ -1920,7 +1912,7 @@ public virtual Task GroupBy_filter_count_OrderBy_count_Select_sum(bool isAsync) })); } - [ConditionalTheory(Skip = "Issue #14935. Cannot eval 'GroupBy([o].CustomerID, [o])'")] + [ConditionalTheory(Skip = "Issue#15249")] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Aggregate_Join(bool isAsync) { @@ -1945,7 +1937,7 @@ join o in os on a.LastOrderID equals o.OrderID entryCount: 126); } - [ConditionalTheory(Skip = "Issue #14935. Cannot eval 'join Order o in value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[Microsoft.EntityFrameworkCore.TestModels.Northwind.Order]) on [a].LastOrderID equals [o].OrderID'")] + [ConditionalTheory(Skip = "Issue#15249")] [MemberData(nameof(IsAsyncData))] public virtual Task Join_GroupBy_Aggregate_multijoins(bool isAsync) { @@ -1971,7 +1963,7 @@ join o in os on a.LastOrderID equals o.OrderID entryCount: 126); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#15249")] [MemberData(nameof(IsAsyncData))] public virtual Task Join_GroupBy_Aggregate_single_join(bool isAsync) { @@ -1996,7 +1988,7 @@ on c.CustomerID equals a.CustomerID entryCount: 63); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#15249")] [MemberData(nameof(IsAsyncData))] public virtual Task Join_GroupBy_Aggregate_with_another_join(bool isAsync) { @@ -2024,7 +2016,7 @@ from g in grouping entryCount: 63); } - [ConditionalTheory(Skip = "Issue #14935. Cannot eval 'join <>f__AnonymousType264`2 i in {from Customer c in value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[Microsoft.EntityFrameworkCore.TestModels.Northwind.Customer]) join <>f__AnonymousType261`2 a in {from IGrouping`2 g in {value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[Microsoft.EntityFrameworkCore.TestModels.Northwind.Order]) => GroupBy([o].CustomerID, [o])} where ({[g] => Count()} > 5) select new <>f__AnonymousType261`2(CustomerID = [g].Key, LastOrderID = {from Order o in [g] select [o].OrderID => Max()})} on [c].CustomerID equals [a].CustomerID select new <>f__AnonymousType264`2(c = [c], LastOrderID = [a].LastOrderID)} on [o].CustomerID equals [i].c.CustomerID'")] + [ConditionalTheory(Skip = "Issue#15249")] [MemberData(nameof(IsAsyncData))] public virtual Task Join_GroupBy_Aggregate_in_subquery(bool isAsync) { @@ -2057,7 +2049,7 @@ on o.CustomerID equals i.c.CustomerID entryCount: 133); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#15249")] [MemberData(nameof(IsAsyncData))] public virtual Task Join_GroupBy_Aggregate_on_key(bool isAsync) { @@ -2083,7 +2075,7 @@ on c.CustomerID equals a.Key entryCount: 63); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_with_result_selector(bool isAsync) { @@ -2105,7 +2097,7 @@ public virtual Task GroupBy_with_result_selector(bool isAsync) e => e.Min + " " + e.Max); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Sum_constant(bool isAsync) { @@ -2114,7 +2106,7 @@ public virtual Task GroupBy_Sum_constant(bool isAsync) os => os.GroupBy(o => o.CustomerID).Select(g => g.Sum(e => 1))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Sum_constant_cast(bool isAsync) { @@ -2123,7 +2115,7 @@ public virtual Task GroupBy_Sum_constant_cast(bool isAsync) os => os.GroupBy(o => o.CustomerID).Select(g => g.Sum(e => 1L))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Distinct_GroupBy_OrderBy_key(bool isAsync) { @@ -2227,7 +2219,7 @@ into g select g.Where(e => e.OrderID < 10300).Count()); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Key_as_part_of_element_selector(bool isAsync) { @@ -2248,7 +2240,7 @@ public virtual Task GroupBy_Key_as_part_of_element_selector(bool isAsync) })); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_composite_Key_as_part_of_element_selector(bool isAsync) { @@ -2948,7 +2940,7 @@ public virtual void Double_GroupBy_with_aggregate() #region ResultOperatorsAfterGroupBy - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Count_after_GroupBy_aggregate(bool isAsync) { @@ -2974,7 +2966,7 @@ into g select g.Where(e => e.OrderID < 10300).Count()).LongCountAsync()); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task MinMax_after_GroupBy_aggregate(bool isAsync) { @@ -2987,15 +2979,30 @@ await AssertMax( os => os.GroupBy(o => o.CustomerID).Select(g => g.Sum(gg => gg.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#16389")] [MemberData(nameof(IsAsyncData))] - public virtual async Task AllAny_after_GroupBy_aggregate(bool isAsync) + public virtual async Task All_after_GroupBy_aggregate(bool isAsync) { await AssertAll( isAsync, os => os.GroupBy(o => o.CustomerID).Select(g => g.Sum(gg => gg.OrderID)), predicate: ee => true); + } + + [ConditionalTheory(Skip = "Issue#16389")] + [MemberData(nameof(IsAsyncData))] + public virtual async Task All_after_GroupBy_aggregate2(bool isAsync) + { + await AssertAll( + isAsync, + os => os.GroupBy(o => o.CustomerID).Select(g => g.Sum(gg => gg.OrderID)), + predicate: ee => ee >= 0); + } + [ConditionalTheory(Skip = "Issue#16389")] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Any_after_GroupBy_aggregate(bool isAsync) + { await AssertAny( isAsync, os => os.GroupBy(o => o.CustomerID).Select(g => g.Sum(gg => gg.OrderID))); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GroupByQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GroupByQuerySqlServerTest.cs index 5ba8d2e601f..3fb87bcb923 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GroupByQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GroupByQuerySqlServerTest.cs @@ -192,6 +192,19 @@ FROM [Orders] AS [o] GROUP BY [o].[CustomerID]"); } + public override async Task GroupBy_aggregate_projecting_conditional_expression_based_on_group_key(bool isAsync) + { + await base.GroupBy_aggregate_projecting_conditional_expression_based_on_group_key(isAsync); + + AssertSql( + @"SELECT CASE + WHEN [o].[OrderDate] IS NULL THEN N'is null' + ELSE N'is not null' +END AS [Key], SUM([o].[OrderID]) AS [Sum] +FROM [Orders] AS [o] +GROUP BY [o].[OrderDate]"); + } + public override async Task GroupBy_anonymous_Select_Average(bool isAsync) { await base.GroupBy_anonymous_Select_Average(isAsync); @@ -437,8 +450,9 @@ public override async Task GroupBy_Dto_as_key_Select_Sum(bool isAsync) await base.GroupBy_Dto_as_key_Select_Sum(isAsync); AssertSql( - @"SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] -FROM [Orders] AS [o0]"); + @"SELECT SUM([o].[OrderID]) AS [Sum], [o].[CustomerID], [o].[EmployeeID] +FROM [Orders] AS [o] +GROUP BY [o].[CustomerID], [o].[EmployeeID]"); } public override async Task GroupBy_Dto_as_element_selector_Select_Sum(bool isAsync) @@ -446,9 +460,9 @@ public override async Task GroupBy_Dto_as_element_selector_Select_Sum(bool isAsy await base.GroupBy_Dto_as_element_selector_Select_Sum(isAsync); AssertSql( - @"SELECT [o].[CustomerID], [o].[EmployeeID] + @"SELECT SUM(CAST([o].[EmployeeID] AS bigint)) AS [Sum], [o].[CustomerID] AS [Key] FROM [Orders] AS [o] -ORDER BY [o].[CustomerID]"); +GROUP BY [o].[CustomerID]"); } public override async Task GroupBy_Composite_Select_Dto_Sum_Min_Key_flattened_Max_Avg(bool isAsync) @@ -456,7 +470,7 @@ public override async Task GroupBy_Composite_Select_Dto_Sum_Min_Key_flattened_Ma await base.GroupBy_Composite_Select_Dto_Sum_Min_Key_flattened_Max_Avg(isAsync); AssertSql( - @"SELECT SUM([o].[OrderID]) AS [Sum], MIN([o].[OrderID]) AS [Min], [o].[CustomerID], [o].[EmployeeID], MAX([o].[OrderID]) AS [Max], AVG(CAST([o].[OrderID] AS float)) AS [Avg] + @"SELECT SUM([o].[OrderID]) AS [Sum], MIN([o].[OrderID]) AS [Min], [o].[CustomerID] AS [CustomerId], [o].[EmployeeID] AS [EmployeeId], MAX([o].[OrderID]) AS [Max], AVG(CAST([o].[OrderID] AS float)) AS [Avg] FROM [Orders] AS [o] GROUP BY [o].[CustomerID], [o].[EmployeeID]"); } @@ -478,7 +492,7 @@ public override async Task GroupBy_Constant_Select_Sum_Min_Key_Max_Avg(bool isAs AssertSql( @"SELECT SUM([t].[OrderID]) AS [Sum], MIN([t].[OrderID]) AS [Min], [t].[Key], MAX([t].[OrderID]) AS [Max], AVG(CAST([t].[OrderID] AS float)) AS [Avg] FROM ( - SELECT [o].*, 2 AS [Key] + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], 2 AS [Key] FROM [Orders] AS [o] ) AS [t] GROUP BY [t].[Key]"); @@ -530,7 +544,7 @@ public override async Task GroupBy_after_predicate_Constant_Select_Sum_Min_Key_M AssertSql( @"SELECT SUM([t].[OrderID]) AS [Sum], MIN([t].[OrderID]) AS [Min], [t].[Key] AS [Random], MAX([t].[OrderID]) AS [Max], AVG(CAST([t].[OrderID] AS float)) AS [Avg] FROM ( - SELECT [o].*, 2 AS [Key] + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], 2 AS [Key] FROM [Orders] AS [o] WHERE [o].[OrderID] > 10500 ) AS [t] @@ -559,7 +573,7 @@ public override async Task GroupBy_param_Select_Sum_Min_Key_Max_Avg(bool isAsync SELECT SUM([t].[OrderID]) AS [Sum], MIN([t].[OrderID]) AS [Min], [t].[Key], MAX([t].[OrderID]) AS [Max], AVG(CAST([t].[OrderID] AS float)) AS [Avg] FROM ( - SELECT [o].*, @__a_0 AS [Key] + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], @__a_0 AS [Key] FROM [Orders] AS [o] ) AS [t] GROUP BY [t].[Key]"); @@ -790,9 +804,9 @@ public override async Task GroupBy_element_selector_complex_aggregate3(bool isAs await base.GroupBy_element_selector_complex_aggregate3(isAsync); AssertSql( - @"SELECT [o].[CustomerID], [o].[OrderID] + @"SELECT SUM([o].[OrderID] + 1) FROM [Orders] AS [o] -ORDER BY [o].[CustomerID]"); +GROUP BY [o].[CustomerID]"); } public override async Task GroupBy_element_selector_complex_aggregate4(bool isAsync) @@ -812,7 +826,7 @@ public override async Task GroupBy_empty_key_Aggregate(bool isAsync) AssertSql( @"SELECT SUM([t].[OrderID]) FROM ( - SELECT [o].*, 1 AS [Key] + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], 1 AS [Key] FROM [Orders] AS [o] ) AS [t] GROUP BY [t].[Key]"); @@ -823,9 +837,9 @@ public override async Task GroupBy_empty_key_Aggregate_Key(bool isAsync) await base.GroupBy_empty_key_Aggregate_Key(isAsync); AssertSql( - @"SELECT SUM([t].[OrderID]) AS [Sum] + @"SELECT SUM([t].[OrderID]) FROM ( - SELECT [o].*, 1 AS [Key] + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], 1 AS [Key] FROM [Orders] AS [o] ) AS [t] GROUP BY [t].[Key]"); @@ -850,7 +864,7 @@ public override async Task OrderBy_Skip_GroupBy_Aggregate(bool isAsync) SELECT AVG(CAST([t].[OrderID] AS float)) FROM ( - SELECT [o].* + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] ORDER BY [o].[OrderID] OFFSET @__p_0 ROWS @@ -867,7 +881,7 @@ public override async Task OrderBy_Take_GroupBy_Aggregate(bool isAsync) SELECT MIN([t].[OrderID]) FROM ( - SELECT TOP(@__p_0) [o].* + SELECT TOP(@__p_0) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] ORDER BY [o].[OrderID] ) AS [t] @@ -884,7 +898,7 @@ public override async Task OrderBy_Skip_Take_GroupBy_Aggregate(bool isAsync) SELECT MAX([t].[OrderID]) FROM ( - SELECT [o].* + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] ORDER BY [o].[OrderID] OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY @@ -899,7 +913,7 @@ public override async Task Distinct_GroupBy_Aggregate(bool isAsync) AssertSql( @"SELECT [t].[CustomerID] AS [Key], COUNT(*) AS [c] FROM ( - SELECT DISTINCT [o].* + SELECT DISTINCT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] ) AS [t] GROUP BY [t].[CustomerID]"); @@ -960,21 +974,21 @@ public override async Task Join_complex_GroupBy_Aggregate(bool isAsync) @__p_1='10' @__p_2='50' -SELECT [t0].[CustomerID] AS [Key], AVG(CAST([t].[OrderID] AS float)) AS [Count] +SELECT [t].[CustomerID] AS [Key], AVG(CAST([t0].[OrderID] AS float)) AS [Count] FROM ( SELECT TOP(@__p_0) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] WHERE [o].[OrderID] < 10400 ORDER BY [o].[OrderDate] -) AS [t] +) AS [t0] INNER JOIN ( SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] - WHERE [c].[CustomerID] NOT IN (N'DRACD', N'FOLKO') + WHERE ([c].[CustomerID] <> N'DRACD') AND ([c].[CustomerID] <> N'FOLKO') ORDER BY [c].[City] OFFSET @__p_1 ROWS FETCH NEXT @__p_2 ROWS ONLY -) AS [t0] ON [t].[CustomerID] = [t0].[CustomerID] -GROUP BY [t0].[CustomerID]"); +) AS [t] ON [t0].[CustomerID] = [t].[CustomerID] +GROUP BY [t].[CustomerID]"); } public override async Task GroupJoin_GroupBy_Aggregate(bool isAsync) @@ -1053,22 +1067,22 @@ public override async Task GroupJoin_complex_GroupBy_Aggregate(bool isAsync) @__p_1='50' @__p_2='100' -SELECT [t0].[CustomerID] AS [Key], AVG(CAST([t0].[OrderID] AS float)) AS [Count] +SELECT [t].[CustomerID] AS [Key], AVG(CAST([t].[OrderID] AS float)) AS [Count] FROM ( - SELECT [c].* + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] - WHERE [c].[CustomerID] NOT IN (N'DRACD', N'FOLKO') + WHERE ([c].[CustomerID] <> N'DRACD') AND ([c].[CustomerID] <> N'FOLKO') ORDER BY [c].[City] OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY -) AS [t] +) AS [t0] INNER JOIN ( SELECT TOP(@__p_2) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] WHERE [o].[OrderID] < 10400 ORDER BY [o].[OrderDate] -) AS [t0] ON [t].[CustomerID] = [t0].[CustomerID] -WHERE [t0].[OrderID] > 10300 -GROUP BY [t0].[CustomerID]"); +) AS [t] ON [t0].[CustomerID] = [t].[CustomerID] +WHERE [t].[OrderID] > 10300 +GROUP BY [t].[CustomerID]"); } public override async Task Self_join_GroupBy_Aggregate(bool isAsync) @@ -1076,9 +1090,9 @@ public override async Task Self_join_GroupBy_Aggregate(bool isAsync) await base.Self_join_GroupBy_Aggregate(isAsync); AssertSql( - @"SELECT [o].[CustomerID] AS [Key], AVG(CAST([o2].[OrderID] AS float)) AS [Count] + @"SELECT [o].[CustomerID] AS [Key], AVG(CAST([o0].[OrderID] AS float)) AS [Count] FROM [Orders] AS [o] -INNER JOIN [Orders] AS [o2] ON [o].[OrderID] = [o2].[OrderID] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] WHERE [o].[OrderID] < 10400 GROUP BY [o].[CustomerID]"); } @@ -1099,7 +1113,18 @@ public override async Task Union_simple_groupby(bool isAsync) { await base.Union_simple_groupby(isAsync); - AssertSql(" "); + AssertSql( + @"SELECT [t].[City] AS [Key], COUNT(*) AS [Total] +FROM ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Customers] AS [c] + WHERE ([c].[ContactTitle] = N'Owner') AND [c].[ContactTitle] IS NOT NULL + UNION + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] + WHERE ([c0].[City] = N'México D.F.') AND [c0].[City] IS NOT NULL +) AS [t] +GROUP BY [t].[City]"); } public override async Task Select_anonymous_GroupBy_Aggregate(bool isAsync) @@ -1131,7 +1156,7 @@ public override async Task GroupBy_OrderBy_key(bool isAsync) @"SELECT [o].[CustomerID] AS [Key], COUNT(*) AS [c] FROM [Orders] AS [o] GROUP BY [o].[CustomerID] -ORDER BY [Key]"); +ORDER BY [o].[CustomerID]"); } public override async Task GroupBy_OrderBy_count(bool isAsync) @@ -1142,7 +1167,7 @@ public override async Task GroupBy_OrderBy_count(bool isAsync) @"SELECT [o].[CustomerID] AS [Key], COUNT(*) AS [Count] FROM [Orders] AS [o] GROUP BY [o].[CustomerID] -ORDER BY [Count], [Key]"); +ORDER BY COUNT(*), [o].[CustomerID]"); } public override async Task GroupBy_OrderBy_count_Select_sum(bool isAsync) @@ -1153,7 +1178,7 @@ public override async Task GroupBy_OrderBy_count_Select_sum(bool isAsync) @"SELECT [o].[CustomerID] AS [Key], SUM([o].[OrderID]) AS [Sum] FROM [Orders] AS [o] GROUP BY [o].[CustomerID] -ORDER BY COUNT(*), [Key]"); +ORDER BY COUNT(*), [o].[CustomerID]"); } public override async Task GroupBy_aggregate_Contains(bool isAsync) @@ -1164,9 +1189,9 @@ public override async Task GroupBy_aggregate_Contains(bool isAsync) @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] WHERE [o].[CustomerID] IN ( - SELECT [e].[CustomerID] AS [Key] - FROM [Orders] AS [e] - GROUP BY [e].[CustomerID] + SELECT [o0].[CustomerID] + FROM [Orders] AS [o0] + GROUP BY [o0].[CustomerID] HAVING COUNT(*) > 30 )"); } @@ -1179,15 +1204,15 @@ public override async Task GroupBy_aggregate_Pushdown(bool isAsync) @"@__p_0='20' @__p_1='4' -SELECT [t].* +SELECT [t].[CustomerID] FROM ( - SELECT TOP(@__p_0) [e].[CustomerID] AS [c] - FROM [Orders] AS [e] - GROUP BY [e].[CustomerID] + SELECT TOP(@__p_0) [o].[CustomerID] + FROM [Orders] AS [o] + GROUP BY [o].[CustomerID] HAVING COUNT(*) > 10 - ORDER BY [c] + ORDER BY [o].[CustomerID] ) AS [t] -ORDER BY [t].[c] +ORDER BY [t].[CustomerID] OFFSET @__p_1 ROWS"); } @@ -1196,7 +1221,19 @@ public override async Task GroupBy_aggregate_Pushdown_followed_by_projecting_Len await base.GroupBy_aggregate_Pushdown_followed_by_projecting_Length(isAsync); AssertSql( - ""); + @"@__p_0='20' +@__p_1='4' + +SELECT CAST(LEN([t].[CustomerID]) AS int) +FROM ( + SELECT TOP(@__p_0) [o].[CustomerID] + FROM [Orders] AS [o] + GROUP BY [o].[CustomerID] + HAVING COUNT(*) > 10 + ORDER BY [o].[CustomerID] +) AS [t] +ORDER BY [t].[CustomerID] +OFFSET @__p_1 ROWS"); } public override async Task GroupBy_aggregate_Pushdown_followed_by_projecting_constant(bool isAsync) @@ -1204,7 +1241,19 @@ public override async Task GroupBy_aggregate_Pushdown_followed_by_projecting_con await base.GroupBy_aggregate_Pushdown_followed_by_projecting_constant(isAsync); AssertSql( - ""); + @"@__p_0='20' +@__p_1='4' + +SELECT 5 +FROM ( + SELECT TOP(@__p_0) [o].[CustomerID] + FROM [Orders] AS [o] + GROUP BY [o].[CustomerID] + HAVING COUNT(*) > 10 + ORDER BY [o].[CustomerID] +) AS [t] +ORDER BY [t].[CustomerID] +OFFSET @__p_1 ROWS"); } public override void GroupBy_Select_sum_over_unmapped_property() @@ -1225,7 +1274,7 @@ public override async Task GroupBy_filter_key(bool isAsync) @"SELECT [o].[CustomerID] AS [Key], COUNT(*) AS [c] FROM [Orders] AS [o] GROUP BY [o].[CustomerID] -HAVING [o].[CustomerID] = N'ALFKI'"); +HAVING ([o].[CustomerID] = N'ALFKI') AND [o].[CustomerID] IS NOT NULL"); } public override async Task GroupBy_filter_count(bool isAsync) @@ -1248,7 +1297,7 @@ public override async Task GroupBy_filter_count_OrderBy_count_Select_sum(bool is FROM [Orders] AS [o] GROUP BY [o].[CustomerID] HAVING COUNT(*) > 4 -ORDER BY [Count], [Key]"); +ORDER BY COUNT(*), [o].[CustomerID]"); } public override async Task GroupBy_Aggregate_Join(bool isAsync) @@ -1388,11 +1437,11 @@ public override async Task Distinct_GroupBy_OrderBy_key(bool isAsync) AssertSql( @"SELECT [t].[CustomerID] AS [Key], COUNT(*) AS [c] FROM ( - SELECT DISTINCT [o].* + SELECT DISTINCT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] ) AS [t] GROUP BY [t].[CustomerID] -ORDER BY [Key]"); +ORDER BY [t].[CustomerID]"); } public override void Select_nested_collection_with_groupby() @@ -1935,29 +1984,43 @@ GROUP BY [o].[CustomerID] ) AS [t]"); } - public override async Task AllAny_after_GroupBy_aggregate(bool isAsync) + public override async Task All_after_GroupBy_aggregate(bool isAsync) { - await base.AllAny_after_GroupBy_aggregate(isAsync); + await base.All_after_GroupBy_aggregate(isAsync); AssertSql( - @"SELECT CASE + @" +CHECK THE SQL +SELECT CASE WHEN NOT EXISTS ( SELECT 1 - FROM ( - SELECT SUM([o].[OrderID]) AS [c] - FROM [Orders] AS [o] - GROUP BY [o].[CustomerID] - ) AS [t] - WHERE 0 = 1) - THEN CAST(1 AS bit) ELSE CAST(0 AS bit) -END", - // - @"SELECT CASE + FROM [Orders] AS [o] + WHERE CAST(0 AS bit) = CAST(1 AS bit) + GROUP BY [o].[CustomerID]) THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END"); + } + + public override async Task All_after_GroupBy_aggregate2(bool isAsync) + { + await base.All_after_GroupBy_aggregate2(isAsync); + + AssertSql(@" "); + } + + public override async Task Any_after_GroupBy_aggregate(bool isAsync) + { + await base.Any_after_GroupBy_aggregate(isAsync); + + AssertSql( + @" +CHECK THE SQL +SELECT CASE WHEN EXISTS ( SELECT 1 FROM [Orders] AS [o] - GROUP BY [o].[CustomerID]) - THEN CAST(1 AS bit) ELSE CAST(0 AS bit) + GROUP BY [o].[CustomerID]) THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) END"); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/IncludeSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/IncludeSqlServerTest.cs index 68523cf6cad..d0a4f22f991 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/IncludeSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/IncludeSqlServerTest.cs @@ -712,11 +712,15 @@ public override void Include_where_skip_take_projection(bool useString) @__p_1='2' SELECT [o].[CustomerID] -FROM [Order Details] AS [o0] -INNER JOIN [Orders] AS [o] ON [o0].[OrderID] = [o].[OrderID] -WHERE [o0].[Quantity] = CAST(10 AS smallint) -ORDER BY [o0].[OrderID], [o0].[ProductID] -OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY"); +FROM ( + SELECT [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] + FROM [Order Details] AS [o0] + WHERE [o0].[Quantity] = CAST(10 AS smallint) + ORDER BY [o0].[OrderID], [o0].[ProductID] + OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY +) AS [t] +INNER JOIN [Orders] AS [o] ON [t].[OrderID] = [o].[OrderID] +ORDER BY [t].[OrderID], [t].[ProductID]"); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs index c7ebea371c6..b5cb4080367 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs @@ -3746,7 +3746,7 @@ public class Stuff #region Bug11818_11831 - [ConditionalFact(Skip = "issue #15249")] + [ConditionalFact] public virtual void GroupJoin_Anonymous_projection_GroupBy_Aggregate_join_elimination() { using (CreateDatabase11818()) @@ -3772,19 +3772,15 @@ from a in grouping.DefaultIfEmpty() .ToList(); AssertSql( - @"SELECT [t].[Name] AS [Key], COUNT(*) + 5 AS [cnt] -FROM [Table] AS [e] -LEFT JOIN ( - SELECT [a].* - FROM [Table] AS [a] - WHERE [a].[Name] IS NOT NULL -) AS [t] ON [e].[Id] = [t].[Id] + @"SELECT [t].[Name] AS [Key], COUNT(*) + 5 AS [cnt] +FROM [Table] AS [t0] +LEFT JOIN [Table] AS [t] ON [t0].[Id] = [t].[Id] GROUP BY [t].[Name]"); } } } - [ConditionalFact(Skip = "issue #15249")] + [ConditionalFact] public virtual void GroupJoin_Anonymous_projection_GroupBy_Aggregate_join_elimination_2() { using (CreateDatabase11818()) @@ -3817,20 +3813,12 @@ from m in grouping2.DefaultIfEmpty() }) .ToList(); - AssertSql( + AssertSql( @"SELECT [t].[Name] AS [MyKey], COUNT(*) + 5 AS [cnt] -FROM [Table] AS [e] -LEFT JOIN ( - SELECT [a].* - FROM [Table] AS [a] - WHERE [a].[Name] IS NOT NULL -) AS [t] ON [e].[Id] = [t].[Id] -LEFT JOIN ( - SELECT [m].* - FROM [Table] AS [m] - WHERE [m].[MaumarEntity11818_Name] IS NOT NULL -) AS [t0] ON [e].[Id] = [t0].[Id] -GROUP BY [t].[Name], [t0].[MaumarEntity11818_Name]"); +FROM [Table] AS [t0] +LEFT JOIN [Table] AS [t] ON [t0].[Id] = [t].[Id] +LEFT JOIN [Table] AS [t1] ON [t0].[Id] = [t1].[Id] +GROUP BY [t].[Name], [t1].[MaumarEntity11818_Name]"); } } }