1

Microsoft have changed ExecuteUpdateAsync to accept non-expression setters parameter in NET 10. Now it works this way:

await context.Blogs.ExecuteUpdateAsync(s =>
{
    s.SetProperty(b => b.Views, 8);

    if (nameChanged)
    {
        s.SetProperty(b => b.Name, "foo");
    }
});

It is great, but I have two functions with operated with unknown entity and one or a few properties:

public static Task<int> ExecuteUpdateAsync(this IQueryable Source, Type EntityType, string FieldName, object? FieldValue, CancellationToken cancellationToken = default)
{
    MethodInfo? UpdateAsyncMethodInfo = typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ExecuteUpdateAsync)) ??
                throw new Exception("EntityFrameworkQueryableExtensions.ExecuteUpdateAsync method is not found");
    Type SetPropertyCallsEntity = typeof(SetPropertyCalls<>).MakeGenericType(EntityType);

    ParameterExpression EntityParam = Expression.Parameter(SetPropertyCallsEntity, "r");
    ParameterExpression PropertyParam = Expression.Parameter(EntityType, "p");

    MemberExpression PropertyExp = Expression.PropertyOrField(PropertyParam, FieldName);
    Expression ValueExp = FieldValue == null ? Expression.Constant(null, PropertyExp.Type) : Expression.Convert(Expression.Constant(FieldValue), PropertyExp.Type);
    // r.SetProperty(p => p.SomeField, value)
    MethodInfo? SetPropertyMethodInfo = (SetPropertyCallsEntity.GetMethods()
                .FirstOrDefault(r => r.Name == nameof(SetPropertyCalls<object>.SetProperty) && r.GetParameters().Any(p => p.ParameterType.BaseType == typeof(object)))?
                .MakeGenericMethod(PropertyExp.Type)) ?? throw new Exception("SetPropertyCalls<>.SetProperty method is not found");
    MethodCallExpression SetBody = Expression.Call(EntityParam, SetPropertyMethodInfo, Expression.Lambda(PropertyExp, PropertyParam), ValueExp);

    LambdaExpression UpdateBody = Expression.Lambda(SetBody, EntityParam);

    return (Task<int>)UpdateAsyncMethodInfo.MakeGenericMethod(Source.ElementType).Invoke(null, [Source, UpdateBody, cancellationToken])!;
}
public static Task<int> ExecuteUpdateAsync(this IQueryable Source, Type EntityType, Dictionary<string, object?> FieldValues, CancellationToken cancellationToken = default)
{
    MethodInfo? UpdateAsyncMethodInfo = typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ExecuteUpdateAsync)) ??
             throw new Exception("EntityFrameworkQueryableExtensions.ExecuteUpdateAsync method is not found");
    Type SetPropertyCallsEntity = typeof(SetPropertyCalls<>).MakeGenericType(EntityType);

    ParameterExpression EntityParam = Expression.Parameter(SetPropertyCallsEntity, "r");
    ParameterExpression PropertyParam = Expression.Parameter(EntityType, "p");
    Expression SetBody = EntityParam;

    foreach (KeyValuePair<string, object?> FieldValue in FieldValues)
    {
        MemberExpression PropertyExp = Expression.PropertyOrField(PropertyParam, FieldValue.Key);
        Expression ValueExp = FieldValue.Value == null ? Expression.Constant(null, PropertyExp.Type) : Expression.Convert(Expression.Constant(FieldValue.Value), PropertyExp.Type);
        // r.SetProperty(p => p.SomeField, value).SetProperty(p => p.AnotherField, AnotherValue)
        MethodInfo? SetPropertyMethodInfo = (SetPropertyCallsEntity.GetMethods()
                    .FirstOrDefault(r => r.Name == nameof(SetPropertyCalls<object>.SetProperty) && r.GetParameters().Any(p => p.ParameterType.BaseType == typeof(object)))?
                    .MakeGenericMethod(PropertyExp.Type)) ?? throw new Exception("SetPropertyCalls<>.SetProperty method is not found");
        SetBody = Expression.Call(SetBody, SetPropertyMethodInfo, Expression.Lambda(PropertyExp, PropertyParam), ValueExp);
    }

    LambdaExpression UpdateBody = Expression.Lambda(SetBody, EntityParam);

    return (Task<int>)UpdateAsyncMethodInfo.MakeGenericMethod(Source.ElementType).Invoke(null, [Source, UpdateBody, cancellationToken])!;
}

What is the way to adopt both to EF Core 10?

To start it, I found UpdateSettersBuilder to replace SetPropertyCalls, and its method SetProperty.

Type SetPropertyBuilder = typeof(UpdateSettersBuilder<>).MakeGenericType(EntityType); 
MethodInfo? SetPropertyMethodInfo = (SetPropertyBuilder.GetMethods() .FirstOrDefault(r => r.Name == nameof(UpdateSettersBuilder<>.SetProperty) && r.GetParameters().Any(p => p.ParameterType.BaseType == typeof(object)))? .MakeGenericMethod(PropertyExp.Type)) ?? throw new Exception("SetPropertyBuilder.SetProperty method is not found");

But what to do next?

6
  • I found UpdateSettersBuilder to replace SetPropertyCalls, and its method SetProperty. ' Type SetPropertyBuilder = typeof(UpdateSettersBuilder<>).MakeGenericType(EntityType); MethodInfo? SetPropertyMethodInfo = (SetPropertyBuilder.GetMethods() .FirstOrDefault(r => r.Name == nameof(UpdateSettersBuilder<>.SetProperty) && r.GetParameters().Any(p => p.ParameterType.BaseType == typeof(object)))? .MakeGenericMethod(PropertyExp.Type)) ?? throw new Exception("SetPropertyBuilder.SetProperty method is not found");' But what to do next? Commented Nov 13 at 20:01
  • Please edit your question in stead of adding comments. Also, it may be better to just describe what you want to achieve. Now we have to infer that from code. Commented Nov 13 at 20:45
  • What does it mean adopt? Previous overload is removed? I've made similar extensions few years ago DynamicRelationalExtensions Commented Nov 13 at 20:51
  • 1
    @Svyatoslav It's a breaking change in EF 10. I don't understand why they immediately removed the previous method, but it is what it is. Commented Nov 14 at 8:06
  • @Stanislav Please explain in a minimal reproducible example what you're trying to achieve to make sure people don't make mistakes in understanding why you had the code you show. Commented Nov 14 at 8:09

1 Answer 1

1

The working answer is below. The first function allows to change any field with name FieldName from any table Source with value FieldValue, if you do not know it permanently.

Second function allow to change a few arbitrary fields similarly to

s.SetProperty(b => b.Views, 8).SetProperty(b => b.Name, "foo");

This is solution for .NET 10, where is not possible to make Expression using SetPropertyCalls. It is replaced with Action<SetPropertyBuilder>.

        public static Task<int> ExecuteUpdateAsync(this IQueryable Source, Type EntityType, string FieldName, object? FieldValue, CancellationToken cancellationToken = default)
        {
            MethodInfo? UpdateAsyncMethodInfo = typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ExecuteUpdateAsync)) ??
                throw new Exception("EntityFrameworkQueryableExtensions.ExecuteUpdateAsync method is not found");
            ParameterExpression PropertyParam = Expression.Parameter(EntityType, "p");
            MemberExpression PropertyExp = Expression.PropertyOrField(PropertyParam, FieldName);
            Type SetPropertyBuilder = typeof(UpdateSettersBuilder<>).MakeGenericType(EntityType);
            MethodInfo SetPropertyMethodInfo = (SetPropertyBuilder.GetMethods()
                .FirstOrDefault(r => r.Name == nameof(UpdateSettersBuilder<>.SetProperty) && r.GetParameters().Any(p => p.ParameterType.BaseType == typeof(object)))?
                .MakeGenericMethod(PropertyExp.Type)) ?? throw new Exception("SetPropertyBuilder.SetProperty method is not found");
            Action<object> BuilderAction = r => SetPropertyMethodInfo.Invoke(r, [Expression.Lambda(PropertyExp, PropertyParam), FieldValue]);
            return (Task<int>)UpdateAsyncMethodInfo.MakeGenericMethod(Source.ElementType).Invoke(null, [Source, BuilderAction, cancellationToken])!;
        }




        public static Task<int> ExecuteUpdateAsync(this IQueryable Source, Type EntityType, Dictionary<string, object?> FieldValues, CancellationToken cancellationToken = default)
        {
            MethodInfo? UpdateAsyncMethodInfo = typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ExecuteUpdateAsync)) ??
                throw new Exception("EntityFrameworkQueryableExtensions.ExecuteUpdateAsync method is not found");
            ParameterExpression PropertyParam = Expression.Parameter(EntityType, "p");
#if NET10_0
            // https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/whatsnew
            Type SetPropertyBuilder = typeof(UpdateSettersBuilder<>).MakeGenericType(EntityType);
            Action<object> BuilderAction = r => { };
            foreach (KeyValuePair<string, object?> FieldValue in FieldValues)
            {
                MemberExpression PropertyExp = Expression.PropertyOrField(PropertyParam, FieldValue.Key);
                MethodInfo? SetPropertyMethodInfo = (SetPropertyBuilder.GetMethods()
                    .FirstOrDefault(r => r.Name == nameof(UpdateSettersBuilder<>.SetProperty) && r.GetParameters().Any(p => p.ParameterType.BaseType == typeof(object)))?
                    .MakeGenericMethod(PropertyExp.Type)) ?? throw new Exception("SetPropertyBuilder.SetProperty method is not found");
                BuilderAction += r => SetPropertyMethodInfo.Invoke(r, [Expression.Lambda(PropertyExp, PropertyParam), FieldValue.Value]);
            }
            return (Task<int>)UpdateAsyncMethodInfo.MakeGenericMethod(Source.ElementType).Invoke(null, [Source, BuilderAction, cancellationToken])!;
#else // NET 9.0
            Type SetPropertyCallsEntity = typeof(SetPropertyCalls<>).MakeGenericType(EntityType);
            ParameterExpression EntityParam = Expression.Parameter(SetPropertyCallsEntity, "r");
            Expression SetBody = EntityParam;
            foreach (KeyValuePair<string, object?> FieldValue in FieldValues)
            {
                MemberExpression PropertyExp = Expression.PropertyOrField(PropertyParam, FieldValue.Key);
                Expression ValueExp = FieldValue.Value == null ? Expression.Constant(null, PropertyExp.Type) : Expression.Convert(Expression.Constant(FieldValue.Value), PropertyExp.Type);
                // r.SetProperty(p => p.SomeField, value).SetProperty(p => p.AnotherField, AnotherValue)
                MethodInfo? SetPropertyMethodInfo = (SetPropertyCallsEntity.GetMethods()
                    .FirstOrDefault(r => r.Name == nameof(SetPropertyCalls<object>.SetProperty) && r.GetParameters().Any(p => p.ParameterType.BaseType == typeof(object)))?
                    .MakeGenericMethod(PropertyExp.Type)) ?? throw new Exception("SetPropertyCalls<>.SetProperty method is not found");
                SetBody = Expression.Call(SetBody, SetPropertyMethodInfo, Expression.Lambda(PropertyExp, PropertyParam), ValueExp);
            }
            LambdaExpression UpdateBody = Expression.Lambda(SetBody, EntityParam);
            return (Task<int>)UpdateAsyncMethodInfo.MakeGenericMethod(Source.ElementType).Invoke(null, [Source, UpdateBody, cancellationToken])!;
#endif
        }
Sign up to request clarification or add additional context in comments.

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.