From e4a6fff93477e857dd398416ee43d59711ad4bd8 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Mon, 25 Dec 2023 11:20:49 +0100 Subject: [PATCH] Fix pruning of UpdateExpression Fixes #31407 --- src/EFCore.Relational/Query/SqlTreePruner.cs | 6 +++++- .../NonSharedModelBulkUpdatesSqlServerTest.cs | 17 +++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/EFCore.Relational/Query/SqlTreePruner.cs b/src/EFCore.Relational/Query/SqlTreePruner.cs index ba51ec5ffa3..3fa83cb18b1 100644 --- a/src/EFCore.Relational/Query/SqlTreePruner.cs +++ b/src/EFCore.Relational/Query/SqlTreePruner.cs @@ -65,9 +65,13 @@ protected override Expression VisitExtension(Expression node) return deleteExpression.Update(PruneTopLevelSelect(deleteExpression.SelectExpression)); case UpdateExpression updateExpression: + // Note that we must visit the setters before we visit the select, since the setters can reference tables inside it. + var visitedSetters = updateExpression.ColumnValueSetters + .Select(e => e with { Value = (SqlExpression)Visit(e.Value) }) + .ToList(); return updateExpression.Update( PruneTopLevelSelect(updateExpression.SelectExpression), - updateExpression.ColumnValueSetters.Select(e => e with { Value = (SqlExpression)Visit(e.Value) }).ToList()); + visitedSetters); // The following remaining cases deal with recursive visitation (i.e. non-top-level things) diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqlServerTest.cs index ecf29e29302..112109b057c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqlServerTest.cs @@ -90,10 +90,19 @@ FROM [Blogs] AS [b] """); } - // #31407 - public override Task Update_non_main_table_in_entity_with_entity_splitting(bool async) - => Assert.ThrowsAnyAsync( - () => base.Update_non_main_table_in_entity_with_entity_splitting(async)); + public override async Task Update_non_main_table_in_entity_with_entity_splitting(bool async) + { + await base.Update_non_main_table_in_entity_with_entity_splitting(async); + + AssertSql( + """ +UPDATE [b0] +SET [b0].[Rating] = CAST(LEN([b0].[Title]) AS int), + [b0].[Title] = CONVERT(varchar(11), [b0].[Rating]) +FROM [Blogs] AS [b] +INNER JOIN [BlogsPart1] AS [b0] ON [b].[Id] = [b0].[Id] +"""); + } public override async Task Delete_entity_with_auto_include(bool async) {