New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ExecuteUpdate for Dynamic Entity #33626
Comments
because all those lambda expressions end up looking like greek to me, i wrote an extension method based on this
then i write the prediates in plain english like this
then
καλή τύχη |
@aslooni ExecuteUpdate accepts the following parameter for the setters: Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> setPropertyCalls In other words, the entire parameter is a single expression tree, whereas you're trying to compile a lambda and insert a reference to the compiled delegate inside it. Here's a code sample that shows dynamic construction of ExecuteUpdate: await using var context = new BlogContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
// First the reflection MethodInfo for the SetProperty() method that we need to call - this isn't trivial (note
// that there are two overloads of SetProperty() - here we get the simpler one that gets a value directly, rather than
// a lambda returning the value.
var setPropertyMethod = typeof(SetPropertyCalls<>)
.MakeGenericType(typeof(Blog))
.GetMethods()
.Single(m => m is
{
Name: nameof(SetPropertyCalls<object>.SetProperty),
IsGenericMethod: true
}
&& m.GetGenericArguments() is [var valueType]
&& m.GetParameters() is
[
_, // Property expression type
{ ParameterType: var valueParameterType },
]
&& valueParameterType == valueType)
.MakeGenericMethod(typeof(string));
var setPropertyParameter = Expression.Parameter(typeof(Blog), "x");
var memberExpression = Expression.Property(setPropertyParameter, "Name");
var setterPropertyLambda = Expression.Lambda<Func<Blog, string>>(memberExpression, setPropertyParameter);
var parameter = Expression.Parameter(typeof(SetPropertyCalls<Blog>), "c");
var setPropertyCall = Expression.Call(
parameter,
setPropertyMethod,
setterPropertyLambda,
Expression.Constant("some name"));
var lambda = Expression.Lambda<Func<SetPropertyCalls<Blog>, SetPropertyCalls<Blog>>>(setPropertyCall, parameter);
await context.Blogs.ExecuteUpdateAsync(lambda);
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer("Server=localhost;Database=test;User=SA;Password=Abcd5678;Connect Timeout=60;ConnectRetryCount=0;Encrypt=false")
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();
}
public class Blog
{
public int Id { get; set; }
public string? Name { get; set; }
} This is indeed quite complex, unfortunately. #32018 tracks making ExecuteUpdate accept multiple expression trees (i.e. for each setter) rather than a single tree as the entire parameter; this would make ExecuteUpdate behave more like what you want, and would simplify constructing dynamic ExecuteUpdate invocations. |
@aslooni am going to close this as a usage issue, and the above should get you what you need (though it's certainly more complex than it should be). |
I am trying to use the ExecuteUpdate method with a dynamic entity. When using the following code snippet where my entity is dynamic, I encounter the following error:
Error message:
Unable to cast object of type 'System.Linq.Expressions.TypedParameterExpression' to type 'System.Linq.Expressions.LambdaExpression'.
How can I resolve this issue?
The text was updated successfully, but these errors were encountered: