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]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
}
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]);
}
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
}
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
}
The working answer is below. ItThe 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]);
}
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
}
The working answer is below. It allows to change any field with name FieldName from any table Source with value FieldValue, if you do not know it permanently.
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])!;
}
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]);
}
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
}
The working answer is below. It allows to change any field with name FieldName from any table Source with value FieldValue, if you do not know it permanently.
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])!;
}