Skip to content

Commit

Permalink
Query : Add support for final GroupBy operator
Browse files Browse the repository at this point in the history
Resolves #19929
  • Loading branch information
smitpatel committed Sep 2, 2022
1 parent 1117c14 commit 9067e43
Show file tree
Hide file tree
Showing 30 changed files with 3,514 additions and 1,077 deletions.
6 changes: 6 additions & 0 deletions src/EFCore.InMemory/Properties/InMemoryStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/EFCore.InMemory/Properties/InMemoryStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@
<data name="UnableToBindMemberToEntityProjection" xml:space="preserve">
<value>Unable to bind '{memberType}' '{member}' to entity projection of '{entityType}'.</value>
</data>
<data name="UncomposedGroupByNotSupported" xml:space="preserve">
<value>A 'GroupBy' operation which is not composed into aggregate or projection of elements is not supported.</value>
</data>
<data name="UpdateConcurrencyException" xml:space="preserve">
<value>Attempted to update or delete an entity that does not exist in the store.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.InMemory.Internal;

namespace Microsoft.EntityFrameworkCore.InMemory.Query.Internal;

/// <summary>
Expand Down Expand Up @@ -91,6 +93,14 @@ protected override Expression VisitExtension(Expression extensionExpression)
/// </summary>
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
if (methodCallExpression.Method.IsGenericMethod
&& (methodCallExpression.Method.GetGenericMethodDefinition() == QueryableMethods.GroupByWithKeySelector
|| methodCallExpression.Method.GetGenericMethodDefinition() == QueryableMethods.GroupByWithKeyElementSelector))
{
throw new InvalidOperationException(
CoreStrings.TranslationFailedWithDetails(methodCallExpression.Print(), InMemoryStrings.UncomposedGroupByNotSupported));
}

if (methodCallExpression.Method.IsGenericMethod
&& methodCallExpression.Arguments.Count == 1
&& methodCallExpression.Arguments[0].Type.TryGetSequenceType() != null
Expand Down

Large diffs are not rendered by default.

517 changes: 517 additions & 0 deletions src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@

namespace Microsoft.EntityFrameworkCore.Query.Internal;


/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public class SingleQueryCollectionContext
public sealed class SingleQueryCollectionContext
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down Expand Up @@ -37,54 +38,54 @@ public SingleQueryCollectionContext(
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual ResultContext ResultContext { get; }
public ResultContext ResultContext { get; }

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object? Parent { get; }
public object? Parent { get; }

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object? Collection { get; }
public object? Collection { get; }

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object[] ParentIdentifier { get; }
public object[] ParentIdentifier { get; }

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object[] OuterIdentifier { get; }
public object[] OuterIdentifier { get; }

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object[]? SelfIdentifier { get; private set; }
public object[]? SelfIdentifier { get; private set; }

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual void UpdateSelfIdentifier(object[]? selfIdentifier)
public void UpdateSelfIdentifier(object[]? selfIdentifier)
=> SelfIdentifier = selfIdentifier;
}
12 changes: 12 additions & 0 deletions src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ public bool MoveNext()
break;
}

// If we are already pointing to next row, we don't need to call Read
if (_resultCoordinator.HasNext == true)
{
continue;
}

if (!_dataReader!.Read())
{
_resultCoordinator.HasNext = false;
Expand Down Expand Up @@ -340,6 +346,12 @@ await _relationalQueryContext.ExecutionStrategy.ExecuteAsync(
break;
}

// If we are already pointing to next row, we don't need to call Read
if (_resultCoordinator.HasNext == true)
{
continue;
}

if (!await _dataReader!.ReadAsync(_cancellationToken).ConfigureAwait(false))
{
_resultCoordinator.HasNext = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public class SplitQueryCollectionContext
public sealed class SplitQueryCollectionContext
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -34,29 +34,29 @@ public SplitQueryCollectionContext(
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual ResultContext ResultContext { get; }
public ResultContext ResultContext { get; }

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object? Parent { get; }
public object? Parent { get; }

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object? Collection { get; }
public object? Collection { get; }

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object[] ParentIdentifier { get; }
public object[] ParentIdentifier { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public class SplitQueryDataReaderContext
public sealed class SplitQueryDataReaderContext
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -29,13 +29,13 @@ public SplitQueryDataReaderContext(
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual bool? HasNext { get; set; }
public bool? HasNext { get; set; }

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual RelationalDataReader DataReader { get; }
public RelationalDataReader DataReader { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ public SplitQueryResultCoordinator()
/// </summary>
public ResultContext ResultContext { get; }

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public bool? HasNext { get; set; }

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ public RelationalCollectionShaperExpression(
Expression parentIdentifier,
Expression outerIdentifier,
Expression selfIdentifier,
IReadOnlyList<ValueComparer>? parentIdentifierValueComparers,
IReadOnlyList<ValueComparer>? outerIdentifierValueComparers,
IReadOnlyList<ValueComparer>? selfIdentifierValueComparers,
IReadOnlyList<ValueComparer> parentIdentifierValueComparers,
IReadOnlyList<ValueComparer> outerIdentifierValueComparers,
IReadOnlyList<ValueComparer> selfIdentifierValueComparers,
Expression innerShaper,
INavigationBase? navigation,
Type elementType)
Expand Down Expand Up @@ -67,17 +67,17 @@ public RelationalCollectionShaperExpression(
/// <summary>
/// The list of value comparers to compare parent identifier.
/// </summary>
public virtual IReadOnlyList<ValueComparer>? ParentIdentifierValueComparers { get; }
public virtual IReadOnlyList<ValueComparer> ParentIdentifierValueComparers { get; }

/// <summary>
/// The list of value comparers to compare outer identifier.
/// </summary>
public virtual IReadOnlyList<ValueComparer>? OuterIdentifierValueComparers { get; }
public virtual IReadOnlyList<ValueComparer> OuterIdentifierValueComparers { get; }

/// <summary>
/// The list of value comparers to compare self identifier.
/// </summary>
public virtual IReadOnlyList<ValueComparer>? SelfIdentifierValueComparers { get; }
public virtual IReadOnlyList<ValueComparer> SelfIdentifierValueComparers { get; }

/// <summary>
/// The expression to create inner elements.
Expand Down
112 changes: 112 additions & 0 deletions src/EFCore.Relational/Query/RelationalGroupByResultExpression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Metadata.Internal;

namespace Microsoft.EntityFrameworkCore.Query;

/// <summary>
/// <para>
/// An expression that represents creation of a grouping for relational provider in
/// <see cref="ShapedQueryExpression.ShaperExpression" />.
/// </para>
/// <para>
/// This type is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// </summary>
public class RelationalGroupByResultExpression : Expression, IPrintableExpression
{
/// <summary>
/// Creates a new instance of the <see cref="RelationalGroupByResultExpression" /> class.
/// </summary>
/// <param name="keyIdentifier">An identifier for the parent element.</param>
/// <param name="keyIdentifierValueComparers">A list of value comparers to compare parent identifier.</param>
/// <param name="keyShaper">An expression used to create individual elements of the collection.</param>
/// <param name="elementShaper">An expression used to create individual elements of the collection.</param>
public RelationalGroupByResultExpression(
Expression keyIdentifier,
IReadOnlyList<ValueComparer> keyIdentifierValueComparers,
Expression keyShaper,
Expression elementShaper)
{
KeyIdentifier = keyIdentifier;
KeyIdentifierValueComparers = keyIdentifierValueComparers;
KeyShaper = keyShaper;
ElementShaper = elementShaper;
Type = typeof(IGrouping<,>).MakeGenericType(keyShaper.Type, elementShaper.Type);
}

/// <summary>
/// The identifier for the grouping key.
/// </summary>
public virtual Expression KeyIdentifier { get; }

/// <summary>
/// The list of value comparers to compare key identifier.
/// </summary>
public virtual IReadOnlyList<ValueComparer> KeyIdentifierValueComparers { get; }

/// <summary>
/// The expression to create the grouping key.
/// </summary>
public virtual Expression KeyShaper { get; }

/// <summary>
/// The expression to create elements in the group.
/// </summary>
public virtual Expression ElementShaper { get; }

/// <inheritdoc />
public override Type Type { get; }

/// <inheritdoc />
public sealed override ExpressionType NodeType
=> ExpressionType.Extension;

/// <inheritdoc />
protected override Expression VisitChildren(ExpressionVisitor visitor)
{
var keyIdentifer = visitor.Visit(KeyIdentifier);
var keyShaper = visitor.Visit(KeyShaper);
var elementShaper = visitor.Visit(ElementShaper);

return Update(keyIdentifer, keyShaper, elementShaper);
}

/// <summary>
/// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will
/// return this expression.
/// </summary>
/// <param name="keyIdentifier">The <see cref="KeyIdentifier" /> property of the result.</param>
/// <param name="keyShaper">The <see cref="KeyShaper" /> property of the result.</param>
/// <param name="elementShaper">The <see cref="ElementShaper" /> property of the result.</param>
/// <returns>This expression if no children changed, or an expression with the updated children.</returns>
public virtual RelationalGroupByResultExpression Update(
Expression keyIdentifier,
Expression keyShaper,
Expression elementShaper)
=> keyIdentifier != KeyIdentifier
|| keyShaper != KeyShaper
|| elementShaper != ElementShaper
? new RelationalGroupByResultExpression(keyIdentifier, KeyIdentifierValueComparers, keyShaper, elementShaper)
: this;

/// <inheritdoc />
void IPrintableExpression.Print(ExpressionPrinter expressionPrinter)
{
expressionPrinter.AppendLine("RelationalGroupByResultExpression:");
using (expressionPrinter.Indent())
{
expressionPrinter.Append("KeyIdentifier:");
expressionPrinter.Visit(KeyIdentifier);
expressionPrinter.AppendLine(",");
expressionPrinter.Append("KeyShaper:");
expressionPrinter.Visit(KeyShaper);
expressionPrinter.AppendLine(",");
expressionPrinter.Append("ElementShaper:");
expressionPrinter.Visit(ElementShaper);
expressionPrinter.AppendLine();
}
}
}
Loading

0 comments on commit 9067e43

Please sign in to comment.