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 Aug 19, 2022
1 parent 7107cf0 commit c70cea6
Show file tree
Hide file tree
Showing 18 changed files with 102 additions and 98 deletions.
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 @@ -446,7 +446,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 @@ -1274,7 +1274,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 @@ -1096,18 +1096,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 @@ -1240,7 +1240,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 @@ -1266,7 +1266,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 @@ -1280,10 +1280,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 @@ -1294,13 +1294,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
Expand Up @@ -18,16 +18,20 @@ 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)
{
Expand Down
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 c70cea6

Please sign in to comment.