Skip to content
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

InvalidOperationException when calling ExecuteUpdate with owned entity & using current value #30528

Closed
mgrosperrin opened this issue Mar 20, 2023 · 4 comments · Fixed by #30571
Assignees
Labels
area-bulkcud closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported Servicing-approved type-bug
Milestone

Comments

@mgrosperrin
Copy link

When calling ExecuteUpdate to conditionnaly update an entity with owned subentity, an System.InvalidOperationException is thrown.

Repro code here: https://github.com/mgrosperrin/repro_issues/tree/efcore-executeupdate-owned-entity
My goal is to update the property only if the user provide a value and to use the current value otherwise (to response to a PATCH API call).

The code in the repo use the null coalescing operator, but I have the same exception with the following code:
updates.SetProperty(e => e.Name, e => e.Name)

Include stack traces

Include the full exception message and stack trace for any exception you encounter.

Use triple-tick fences for stack traces. For example:

System.InvalidOperationException: 'The LINQ expression 'DbSet<Entity>()
    .Select(e => IncludeExpression(
        EntityExpression:
        e, 
        NavigationExpression:
        EF.Property<SubEntity>(e, "Owned"), Owned)
    )
    .ExecuteUpdate(updates => updates.SetProperty<string>(
        propertyExpression: e => e.Name, 
        valueExpression: e => __newName_0 ?? e.Name).SetProperty<string>(
        propertyExpression: e => e.DisplayName, 
        valueExpression: e => __newDisplayName_1 ?? e.DisplayName))' could not be translated. Additional information: The following 'SetProperty' failed to translate: 'SetProperty(e => e.DisplayName, e => __newDisplayName_1 ?? e.DisplayName)'.  See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.'

Include provider and version information

EF Core version: 7.0.4
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 7.0
Operating system: Windows 11
IDE: Visual Studio 2022 17.5.2

@roji
Copy link
Member

roji commented Mar 20, 2023

Minimal repro with 7.0.4:

await using var ctx = new BlogContext();
await ctx.Database.EnsureDeletedAsync();
await ctx.Database.EnsureCreatedAsync();

ctx.Entities.ExecuteUpdate(updates => updates.SetProperty(e => e.Name, e => e.Name));

class BlogContext : DbContext
{
    public DbSet<Blog> Entities { 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; }
    public BlogDetails Owned { get; set; }
}

[Owned]
public class BlogDetails
{
    public string Value { get; set; }
}

Exception:

Unhandled exception. System.InvalidOperationException: The LINQ expression 'DbSet<Blog>()
    .Select(b => IncludeExpression(
        EntityExpression:
        b, 
        NavigationExpression:
        EF.Property<BlogDetails>(b, "Owned"), Owned)
    )
    .ExecuteUpdate(updates => updates.SetProperty<string>(
        propertyExpression: e => e.Name, 
        valueExpression: e => e.Name))' could not be translated. Additional information: The following 'SetProperty' failed to translate: 'SetProperty(e => e.Name, e => e.Name)'.  See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
   at Microsoft.EntityFrameworkCore.RelationalQueryableExtensions.ExecuteUpdate[TSource](IQueryable`1 source, Expression`1 setPropertyCalls)
   at Program.<Main>$(String[] args) in /home/roji/projects/test/Program.cs:line 13
   at Program.<Main>$(String[] args) in /home/roji/projects/test/Program.cs:line 13
   at Program.<Main>(String[] args)

@ajcvickers
Copy link
Member

@roji

This works:

await context.Entities.ExecuteUpdateAsync(x => x.SetProperty(e => e.Name, "X"));

And this works:

var newVal = "X";
await context.Entities.ExecuteUpdateAsync(x => x.SetProperty(e => e.Name, newVal));

But this throws:

await context.Entities.ExecuteUpdateAsync(x => x.SetProperty(e => e.Name, e => e.Name));

@roji
Copy link
Member

roji commented Mar 23, 2023

@ajcvickers thanks, I'll look into this soon. I may have a hunch.

@roji
Copy link
Member

roji commented Mar 24, 2023

Submitted #30571, definitely a good candidate for patching.

roji added a commit to roji/efcore that referenced this issue Mar 24, 2023
@roji roji added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Mar 24, 2023
@ajcvickers ajcvickers added this to the 7.0.x milestone Mar 24, 2023
roji added a commit that referenced this issue Mar 25, 2023
* Fully prune IncludeExpression before ExecuteUpdate, not just for the
  property lambda
* Prune also non-owned Includes

Fixes #30572
Fixes #30528
@roji roji reopened this Mar 25, 2023
roji added a commit to roji/efcore that referenced this issue Mar 25, 2023
* Fully prune IncludeExpression before ExecuteUpdate, not just for the
  property lambda
* Prune also non-owned Includes

Fixes dotnet#30572
Fixes dotnet#30528

(cherry picked from commit 4c6f854)
@ajcvickers ajcvickers modified the milestones: 7.0.x, 7.0.6 Mar 28, 2023
@ajcvickers ajcvickers modified the milestones: 7.0.6, 7.0.7 Jun 22, 2023
roji added a commit to roji/efcore that referenced this issue Nov 17, 2023
* Fully prune IncludeExpression before ExecuteUpdate, not just for the
  property lambda
* Prune also non-owned Includes

Fixes dotnet#30572
Fixes dotnet#30528

(cherry picked from commit 4c6f854)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-bulkcud closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported Servicing-approved type-bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants