Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query: API review changes #28023

Merged
1 commit merged into from
May 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,6 @@ public virtual GroupByShaperExpression ApplyGrouping(

return new GroupByShaperExpression(
groupingKey,
shaperExpression,
new ShapedQueryExpression(
clonedInMemoryQueryExpression,
new QueryExpressionReplacingExpressionVisitor(this, clonedInMemoryQueryExpression).Visit(shaperExpression)));
Expand Down
103 changes: 77 additions & 26 deletions src/EFCore.Relational/Query/EnumerableExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;

namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions;
namespace Microsoft.EntityFrameworkCore.Query;

/// <summary>
/// <para>
Expand All @@ -16,96 +17,122 @@ namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions;
/// </summary>
public class EnumerableExpression : Expression, IPrintableExpression
{
private readonly List<OrderingExpression> _orderings = new();

/// <summary>
/// Creates a new instance of the <see cref="EnumerableExpression" /> class.
/// </summary>
/// <param name="selector">The underlying sql expression being enumerated.</param>
public EnumerableExpression(Expression selector)
{
Selector = selector;
IsDistinct = false;
Predicate = null;
Orderings = new List<OrderingExpression>();
}

private EnumerableExpression(
Expression selector,
bool distinct,
SqlExpression? predicate,
IReadOnlyList<OrderingExpression> orderings)
{
Selector = selector;
IsDistinct = distinct;
Predicate = predicate;
Orderings = orderings;
}

/// <summary>
/// The underlying expression being enumerated.
/// </summary>
public virtual Expression Selector { get; private set; }
public virtual Expression Selector { get; }

/// <summary>
/// The value indicating if distinct operator is applied on the enumerable or not.
/// </summary>
public virtual bool IsDistinct { get; private set; }
public virtual bool IsDistinct { get; }

/// <summary>
/// The value indicating any predicate applied on the enumerable.
/// </summary>
public virtual SqlExpression? Predicate { get; private set; }
public virtual SqlExpression? Predicate { get; }

/// <summary>
/// The list of orderings to be applied to the enumerable.
/// </summary>
public virtual IReadOnlyList<OrderingExpression> Orderings => _orderings;
public virtual IReadOnlyList<OrderingExpression> Orderings { get; }


/// <summary>
/// Applies new selector to the <see cref="EnumerableExpression" />.
/// </summary>
public virtual void ApplySelector(Expression expression)
{
Selector = expression;
}
/// <returns>The new expression with specified component updated.</returns>
public virtual EnumerableExpression ApplySelector(Expression expression)
=> new(expression, IsDistinct, Predicate, Orderings);

/// <summary>
/// Applies DISTINCT operator to the selector of the <see cref="EnumerableExpression" />.
/// </summary>
public virtual void ApplyDistinct()
{
IsDistinct = true;
}
/// <returns>The new expression with specified component updated.</returns>
public virtual EnumerableExpression ApplyDistinct()
=> new(Selector, distinct: true, Predicate, Orderings);

/// <summary>
/// Applies filter predicate to the <see cref="EnumerableExpression" />.
/// </summary>
/// <param name="sqlExpression">An expression to use for filtering.</param>
public virtual void ApplyPredicate(SqlExpression sqlExpression)
/// <returns>The new expression with specified component updated.</returns>
public virtual EnumerableExpression ApplyPredicate(SqlExpression sqlExpression)
{
if (sqlExpression is SqlConstantExpression sqlConstant
&& sqlConstant.Value is bool boolValue
&& boolValue)
{
return;
return this;
}

Predicate = Predicate == null
var predicate = Predicate == null
? sqlExpression
: new SqlBinaryExpression(
ExpressionType.AndAlso,
Predicate,
sqlExpression,
typeof(bool),
sqlExpression.TypeMapping);

return new(Selector, IsDistinct, predicate, Orderings);
}

/// <summary>
/// Applies ordering to the <see cref="EnumerableExpression" />. This overwrites any previous ordering specified.
/// </summary>
/// <param name="orderingExpression">An ordering expression to use for ordering.</param>
public virtual void ApplyOrdering(OrderingExpression orderingExpression)
/// <returns>The new expression with specified component updated.</returns>
public virtual EnumerableExpression ApplyOrdering(OrderingExpression orderingExpression)
{
_orderings.Clear();
AppendOrdering(orderingExpression);
var orderings = new List<OrderingExpression>();
AppendOrdering(orderings, orderingExpression);

return new EnumerableExpression(Selector, IsDistinct, Predicate, orderings);
}

/// <summary>
/// Appends ordering to the existing orderings of the <see cref="EnumerableExpression" />.
/// </summary>
/// <param name="orderingExpression">An ordering expression to use for ordering.</param>
public virtual void AppendOrdering(OrderingExpression orderingExpression)
/// <returns>The new expression with specified component updated.</returns>
public virtual EnumerableExpression AppendOrdering(OrderingExpression orderingExpression)
{
var orderings = Orderings.ToList();
AppendOrdering(orderings, orderingExpression);

return new EnumerableExpression(Selector, IsDistinct, Predicate, orderings);
}

private static void AppendOrdering(List<OrderingExpression> orderings, OrderingExpression orderingExpression)
{
if (!_orderings.Any(o => o.Expression.Equals(orderingExpression.Expression)))
if (!orderings.Any(o => o.Expression.Equals(orderingExpression.Expression)))
{
_orderings.Add(orderingExpression.Update(orderingExpression.Expression));
orderings.Add(orderingExpression.Update(orderingExpression.Expression));
}
}

Expand Down Expand Up @@ -151,8 +178,32 @@ public virtual void Print(ExpressionPrinter expressionPrinter)
}

/// <inheritdoc />
public override bool Equals(object? obj) => ReferenceEquals(this, obj);
public override bool Equals(object? obj)
=> obj != null
&& (ReferenceEquals(this, obj)
|| obj is EnumerableExpression enumerableExpression
&& Equals(enumerableExpression));

private bool Equals(EnumerableExpression enumerableExpression)
=> IsDistinct == enumerableExpression.IsDistinct
&& (Predicate == null
? enumerableExpression.Predicate == null
: Predicate.Equals(enumerableExpression.Predicate))
&& ExpressionEqualityComparer.Instance.Equals(Selector, enumerableExpression.Selector)
&& Orderings.SequenceEqual(enumerableExpression.Orderings);

/// <inheritdoc />
public override int GetHashCode() => RuntimeHelpers.GetHashCode(this);
public override int GetHashCode()
{
var hashCode = new HashCode();
hashCode.Add(IsDistinct);
hashCode.Add(Selector);
hashCode.Add(Predicate);
foreach (var ordering in Orderings)
{
hashCode.Add(ordering);
}

return hashCode.ToHashCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.EntityFrameworkCore.Query;

/// <summary>
/// <para>
/// An expression that represents creation of a grouping element in <see cref="ShapedQueryExpression.ShaperExpression" />
/// for relational providers.
/// </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 RelationalGroupByShaperExpression : GroupByShaperExpression
{
/// <summary>
/// Creates a new instance of the <see cref="RelationalGroupByShaperExpression" /> class.
/// </summary>
/// <param name="keySelector">An expression representing key selector for the grouping result.</param>
/// <param name="elementSelector">An expression representing element selector for the grouping result.</param>
/// <param name="groupingEnumerable">An expression representing subquery for enumerable over the grouping result.</param>
public RelationalGroupByShaperExpression(
Expression keySelector,
Expression elementSelector,
ShapedQueryExpression groupingEnumerable)
: base(keySelector, groupingEnumerable)
{
ElementSelector = elementSelector;
}

/// <summary>
/// The expression representing the element selector for this grouping result.
/// </summary>
public virtual Expression ElementSelector { get; }

/// <inheritdoc />
protected override Expression VisitChildren(ExpressionVisitor visitor)
=> throw new InvalidOperationException(
CoreStrings.VisitIsNotAllowed($"{nameof(RelationalGroupByShaperExpression)}.{nameof(VisitChildren)}"));

/// <inheritdoc />
public override void Print(ExpressionPrinter expressionPrinter)
{
expressionPrinter.AppendLine($"{nameof(RelationalGroupByShaperExpression)}:");
expressionPrinter.Append("KeySelector: ");
expressionPrinter.Visit(KeySelector);
expressionPrinter.AppendLine(", ");
expressionPrinter.Append("ElementSelector: ");
expressionPrinter.Visit(ElementSelector);
expressionPrinter.AppendLine(", ");
expressionPrinter.Append("GroupingEnumerable:");
expressionPrinter.Visit(GroupingEnumerable);
expressionPrinter.AppendLine();
}
}
Loading