Skip to content

Commit

Permalink
Query: API review changes
Browse files Browse the repository at this point in the history
Part of #27588
  • Loading branch information
smitpatel committed May 13, 2022
1 parent deb3a87 commit 607b33d
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 115 deletions.
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();
}
}
57 changes: 57 additions & 0 deletions src/EFCore.Relational/Query/RelationalGroupByShaperExpression.cs
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

0 comments on commit 607b33d

Please sign in to comment.