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?