Skip to content

Commit

Permalink
Query: API review changes (#28833)
Browse files Browse the repository at this point in the history
  • Loading branch information
smitpatel authored Aug 22, 2022
1 parent c9f39b0 commit b5a649f
Show file tree
Hide file tree
Showing 19 changed files with 133 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ protected override void ProcessModelAnnotations(
/// <param name="runtimeEntityType">The target entity type that will contain the annotations.</param>
/// <param name="runtime">Indicates whether the given annotations are runtime annotations.</param>
protected override void ProcessEntityTypeAnnotations(
IDictionary<string, object?> annotations,
Dictionary<string, object?> annotations,
IEntityType entityType,
RuntimeEntityType runtimeEntityType,
bool runtime)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,13 +342,13 @@ internal static readonly MethodInfo ExecuteDeleteMethodInfo
/// </para>
/// </remarks>
/// <param name="source">The source query.</param>
/// <param name="setPropertyStatements">A collection of set property statements specifying properties to update.</param>
/// <param name="setPropertyCalls">A collection of set property statements specifying properties to update.</param>
/// <returns>The total number of rows updated in the database.</returns>
public static int ExecuteUpdate<TSource>(
this IQueryable<TSource> source,
Expression<Func<SetPropertyStatements<TSource>, SetPropertyStatements<TSource>>> setPropertyStatements)
Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> setPropertyCalls)
=> source.Provider.Execute<int>(
Expression.Call(ExecuteUpdateMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression, setPropertyStatements));
Expression.Call(ExecuteUpdateMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression, setPropertyCalls));

/// <summary>
/// Asynchronously updates database rows for the entity instances which match the LINQ query from the database.
Expand All @@ -366,17 +366,17 @@ public static int ExecuteUpdate<TSource>(
/// </para>
/// </remarks>
/// <param name="source">The source query.</param>
/// <param name="setPropertyStatements">A collection of set property statements specifying properties to update.</param>
/// <param name="setPropertyCalls">A collection of set property statements specifying properties to update.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns>The total number of rows updated in the database.</returns>
public static Task<int> ExecuteUpdateAsync<TSource>(
this IQueryable<TSource> source,
Expression<Func<SetPropertyStatements<TSource>, SetPropertyStatements<TSource>>> setPropertyStatements,
Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> setPropertyCalls,
CancellationToken cancellationToken = default)
=> source.Provider is IAsyncQueryProvider provider
? provider.ExecuteAsync<Task<int>>(
Expression.Call(
ExecuteUpdateMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression, setPropertyStatements), cancellationToken)
ExecuteUpdateMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression, setPropertyCalls), cancellationToken)
: throw new InvalidOperationException(CoreStrings.IQueryableProviderNotAsync);

internal static readonly MethodInfo ExecuteUpdateMethodInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ protected override void ProcessModelAnnotations(
/// <param name="runtimeEntityType">The target entity type that will contain the annotations.</param>
/// <param name="runtime">Indicates whether the given annotations are runtime annotations.</param>
protected override void ProcessEntityTypeAnnotations(
IDictionary<string, object?> annotations,
Dictionary<string, object?> annotations,
IEntityType entityType,
RuntimeEntityType runtimeEntityType,
bool runtime)
Expand Down Expand Up @@ -405,7 +405,7 @@ protected virtual void ProcessPropertyOverridesAnnotations(
/// <param name="runtimeKey">The target key that will contain the annotations.</param>
/// <param name="runtime">Indicates whether the given annotations are runtime annotations.</param>
protected override void ProcessKeyAnnotations(
IDictionary<string, object?> annotations,
Dictionary<string, object?> annotations,
IKey key,
RuntimeKey runtimeKey,
bool runtime)
Expand Down

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

2 changes: 1 addition & 1 deletion src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@
<value>Unable to translate a collection subquery in a projection since either parent or the subquery doesn't project necessary information required to uniquely identify it and correctly generate results on the client side. This can happen when trying to correlate on keyless entity type. This can also happen for some cases of projection before 'Distinct' or some shapes of grouping key in case of 'GroupBy'. These should either contain all key properties of the entity that the operation is applied on, or only contain simple property access expressions.</value>
</data>
<data name="InvalidArgumentToExecuteUpdate" xml:space="preserve">
<value>The 'setPropertyStatements' argument to 'ExecuteUpdate' may only contain a chain of 'SetProperty' expressing the properties to be updated.</value>
<value>The 'setPropertyCalls' argument to 'ExecuteUpdate' may only contain a chain of 'SetProperty' expressing the properties to be updated.</value>
</data>
<data name="InvalidCommandTimeout" xml:space="preserve">
<value>The specified 'CommandTimeout' value '{value}' is not valid. It must be a positive number.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class SelectExpressionPruningExpressionVisitor : ExpressionVisitor
case UpdateExpression updateExpression:
return updateExpression.Update(
updateExpression.SelectExpression.Prune(),
updateExpression.SetColumnValues.Select(e => new SetColumnValue(e.Column, (SqlExpression)Visit(e.Value))).ToList());
updateExpression.ColumnValueSetters.Select(e => new ColumnValueSetter(e.Column, (SqlExpression)Visit(e.Value))).ToList());

default:
return base.Visit(expression);
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore.Relational/Query/QuerySqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1246,7 +1246,7 @@ protected override Expression VisitUpdate(UpdateExpression updateExpression)
using (_relationalCommandBuilder.Indent())
{
_relationalCommandBuilder.Append("SET ");
GenerateList(updateExpression.SetColumnValues,
GenerateList(updateExpression.ColumnValueSetters,
e =>
{
_relationalCommandBuilder.Append($"{_sqlGenerationHelper.DelimitIdentifier(e.Column.Name)} = ");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1104,18 +1104,18 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
}

/// <summary>
/// Translates <see cref="RelationalQueryableExtensions.ExecuteUpdate{TSource}(IQueryable{TSource}, Expression{Func{SetPropertyStatements{TSource}, SetPropertyStatements{TSource}}})" /> method
/// Translates <see cref="RelationalQueryableExtensions.ExecuteUpdate{TSource}(IQueryable{TSource}, Expression{Func{SetPropertyCalls{TSource}, SetPropertyCalls{TSource}}})" /> method
/// over the given source.
/// </summary>
/// <param name="source">The shaped query on which the operator is applied.</param>
/// <param name="setPropertyStatements">The lambda expression containing <see cref="SetPropertyStatements{TSource}.SetProperty{TProperty}(Expression{Func{TSource, TProperty}}, Expression{Func{TSource, TProperty}})"/> statements.</param>
/// <param name="setPropertyCalls">The lambda expression containing <see cref="SetPropertyCalls{TSource}.SetProperty{TProperty}(Expression{Func{TSource, TProperty}}, Expression{Func{TSource, TProperty}})"/> statements.</param>
/// <returns>The non query after translation.</returns>
protected virtual NonQueryExpression? TranslateExecuteUpdate(
ShapedQueryExpression source,
LambdaExpression setPropertyStatements)
LambdaExpression setPropertyCalls)
{
var propertyValueLambdaExpressions = new List<(LambdaExpression, LambdaExpression)>();
PopulateSetPropertyStatements(setPropertyStatements.Body, propertyValueLambdaExpressions, setPropertyStatements.Parameters[0]);
PopulateSetPropertyCalls(setPropertyCalls.Body, propertyValueLambdaExpressions, setPropertyCalls.Parameters[0]);
if (TranslationErrorDetails != null)
{
return null;
Expand Down Expand Up @@ -1248,7 +1248,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
List<(LambdaExpression, LambdaExpression)> propertyValueLambdaExpressions,
List<Expression>? leftExpressions)
{
var setColumnValues = new List<SetColumnValue>();
var columnValueSetters = new List<ColumnValueSetter>();
for (var i = 0; i < propertyValueLambdaExpressions.Count; i++)
{
var (propertyExpression, valueExpression) = propertyValueLambdaExpressions[i];
Expand All @@ -1274,7 +1274,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
var translation = visitor._sqlTranslator.Translate(setter);
if (translation is SqlBinaryExpression { OperatorType: ExpressionType.Equal, Left: ColumnExpression column } sqlBinaryExpression)
{
setColumnValues.Add(new SetColumnValue(column, sqlBinaryExpression.Right));
columnValueSetters.Add(new ColumnValueSetter(column, sqlBinaryExpression.Right));
}
else
{
Expand All @@ -1288,10 +1288,10 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
selectExpression.ReplaceProjection(new List<Expression>());
selectExpression.ApplyProjection();

return new NonQueryExpression(new UpdateExpression(tableExpression, selectExpression, setColumnValues));
return new NonQueryExpression(new UpdateExpression(tableExpression, selectExpression, columnValueSetters));
}

void PopulateSetPropertyStatements(
void PopulateSetPropertyCalls(
Expression expression, List<(LambdaExpression, LambdaExpression)> list, ParameterExpression parameter)
{
switch (expression)
Expand All @@ -1302,13 +1302,13 @@ void PopulateSetPropertyStatements(

case MethodCallExpression methodCallExpression
when methodCallExpression.Method.IsGenericMethod
&& methodCallExpression.Method.Name == nameof(SetPropertyStatements<int>.SetProperty)
&& methodCallExpression.Method.Name == nameof(SetPropertyCalls<int>.SetProperty)
&& methodCallExpression.Method.DeclaringType!.IsGenericType
&& methodCallExpression.Method.DeclaringType.GetGenericTypeDefinition() == typeof(SetPropertyStatements<>):
&& methodCallExpression.Method.DeclaringType.GetGenericTypeDefinition() == typeof(SetPropertyCalls<>):

list.Add((methodCallExpression.Arguments[0].UnwrapLambdaFromQuote(),
methodCallExpression.Arguments[1].UnwrapLambdaFromQuote()));
PopulateSetPropertyStatements(methodCallExpression.Object!, list, parameter);
PopulateSetPropertyCalls(methodCallExpression.Object!, list, parameter);

break;

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 System.ComponentModel;

namespace Microsoft.EntityFrameworkCore.Query;

/// <summary>
Expand All @@ -18,19 +20,54 @@ namespace Microsoft.EntityFrameworkCore.Query;
/// and <see href="https://aka.ms/efcore-docs-how-query-works">How EF Core queries work</see> for more information and examples.
/// </remarks>
/// <typeparam name="TSource">The type of source element on which ExecuteUpdate operation is being applied.</typeparam>
public sealed class SetPropertyStatements<TSource>
public sealed class SetPropertyCalls<TSource>
{
private SetPropertyCalls()
{
}

/// <summary>
/// Specifies a property and corresponding value it should be updated to in ExecuteUpdate method.
/// </summary>
/// <typeparam name="TProperty">The type of property.</typeparam>
/// <param name="propertyExpression">A property access expression.</param>
/// <param name="valueExpression">A value expression.</param>
/// <returns>The same instance so that multiple calls to <see cref="SetProperty{TProperty}(Expression{Func{TSource, TProperty}}, Expression{Func{TSource, TProperty}})"/> can be chained.</returns>
public SetPropertyStatements<TSource> SetProperty<TProperty>(
public SetPropertyCalls<TSource> SetProperty<TProperty>(
Expression<Func<TSource, TProperty>> propertyExpression,
Expression<Func<TSource, TProperty>> valueExpression)
{
throw new InvalidOperationException(RelationalStrings.SetPropertyMethodInvoked);
}

#region Hidden System.Object members

/// <summary>
/// Returns a string that represents the current object.
/// </summary>
/// <returns>A string that represents the current object.</returns>
[EditorBrowsable(EditorBrowsableState.Never)]
public override string? ToString()
=> base.ToString();

/// <summary>
/// Determines whether the specified object is equal to the current object.
/// </summary>
/// <param name="obj">The object to compare with the current object.</param>
/// <returns><see langword="true" /> if the specified object is equal to the current object; otherwise, <see langword="false" />.</returns>
[EditorBrowsable(EditorBrowsableState.Never)]
// ReSharper disable once BaseObjectEqualsIsObjectEquals
public override bool Equals(object? obj)
=> base.Equals(obj);

/// <summary>
/// Serves as the default hash function.
/// </summary>
/// <returns>A hash code for the current object.</returns>
[EditorBrowsable(EditorBrowsableState.Never)]
// ReSharper disable once BaseObjectGetHashCodeCallInGetHashCode
public override int GetHashCode()
=> base.GetHashCode();

#endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions;
/// not used in application code.
/// </para>
/// </summary>
public class SetColumnValue
public class ColumnValueSetter
{
/// <summary>
/// Creates a new instance of the <see cref="SetColumnValue" /> class.
/// Creates a new instance of the <see cref="ColumnValueSetter" /> class.
/// </summary>
/// <param name="column">A column to be updated.</param>
/// <param name="value">A value to be assigned to the column.</param>
public SetColumnValue(ColumnExpression column, SqlExpression value)
public ColumnValueSetter(ColumnExpression column, SqlExpression value)
{
Column = column;
Value = value;
Expand All @@ -39,12 +39,12 @@ public SetColumnValue(ColumnExpression column, SqlExpression value)
public override bool Equals(object? obj)
=> obj != null
&& (ReferenceEquals(this, obj)
|| obj is SetColumnValue setColumnValue
&& Equals(setColumnValue));
|| obj is ColumnValueSetter columnValueSetter
&& Equals(columnValueSetter));

private bool Equals(SetColumnValue setColumnValue)
=> Column == setColumnValue.Column
&& Value == setColumnValue.Value;
private bool Equals(ColumnValueSetter columnValueSetter)
=> Column == columnValueSetter.Column
&& Value == columnValueSetter.Value;

/// <inheritdoc />
public override int GetHashCode() => HashCode.Combine(Column, Value);
Expand Down
Loading

0 comments on commit b5a649f

Please sign in to comment.