diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs
index bae6307727a..0d7ddc80370 100644
--- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs
@@ -17,10 +17,7 @@ public partial class RelationalQueryableMethodTranslatingExpressionVisitor
/// The non query after translation.
protected virtual NonQueryExpression? TranslateExecuteDelete(ShapedQueryExpression source)
{
- if (source.ShaperExpression is IncludeExpression includeExpression)
- {
- source = source.UpdateShaperExpression(PruneIncludes(includeExpression));
- }
+ source = source.UpdateShaperExpression(new IncludePruner().Visit(source.ShaperExpression));
if (source.ShaperExpression is not StructuralTypeShaperExpression { StructuralType: IEntityType entityType } shaper)
{
diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs
index 9473fb1ab9a..9b2cc06f5f3 100644
--- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs
@@ -33,10 +33,7 @@ public partial class RelationalQueryableMethodTranslatingExpressionVisitor
{
// Our source may have IncludeExpressions because of owned entities or auto-include; unwrap these, as they're meaningless for
// ExecuteUpdate's lambdas. Note that we don't currently support updates across tables.
- if (source.ShaperExpression is IncludeExpression includeExpression)
- {
- source = source.UpdateShaperExpression(PruneIncludes(includeExpression));
- }
+ source = source.UpdateShaperExpression(new IncludePruner().Visit(source.ShaperExpression));
var setters = new List<(LambdaExpression PropertySelector, Expression ValueExpression)>();
PopulateSetPropertyCalls(setPropertyCalls.Body, setters, setPropertyCalls.Parameters[0]);
diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
index 4b9c1b147e3..08cfbfcd9d0 100644
--- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
@@ -1342,16 +1342,15 @@ private Expression RemapLambdaBody(ShapedQueryExpression shapedQueryExpression,
private Expression ExpandSharedTypeEntities(SelectExpression selectExpression, Expression lambdaBody)
=> _sharedTypeEntityExpandingExpressionVisitor.Expand(selectExpression, lambdaBody);
- private static Expression PruneIncludes(IncludeExpression includeExpression)
+ private sealed class IncludePruner : ExpressionVisitor
{
- if (includeExpression.Navigation is ISkipNavigation or not INavigation)
- {
- return includeExpression;
- }
-
- return includeExpression.EntityExpression is IncludeExpression innerIncludeExpression
- ? PruneIncludes(innerIncludeExpression)
- : includeExpression.EntityExpression;
+ protected override Expression VisitExtension(Expression node)
+ => node switch
+ {
+ IncludeExpression { Navigation: ISkipNavigation or not INavigation } i => i,
+ IncludeExpression i => Visit(i.EntityExpression),
+ _ => base.VisitExtension(node)
+ };
}
private sealed class SharedTypeEntityExpandingExpressionVisitor : ExpressionVisitor
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NonSharedModelBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NonSharedModelBulkUpdatesTestBase.cs
index b07fc573589..f4b7723e9d9 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NonSharedModelBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NonSharedModelBulkUpdatesTestBase.cs
@@ -122,6 +122,24 @@ await AssertUpdate(
rowsAffectedCount: 0);
}
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Update_non_owned_property_on_entity_with_owned_in_join(bool async)
+ {
+ var contextFactory = await InitializeAsync(
+ onModelCreating: mb =>
+ {
+ mb.Entity().OwnsOne(o => o.OwnedReference);
+ });
+
+ await AssertUpdate(
+ async,
+ contextFactory.CreateContext,
+ ss => ss.Set().Join(ss.Set(), o => o.Id, i => i.Id, (o, i) => new { Outer = o, Inner = i}),
+ s => s.SetProperty(t => t.Outer.Title, "NewValue"),
+ rowsAffectedCount: 0);
+ }
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Update_owned_and_non_owned_properties_with_table_sharing(bool async)
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqlServerTest.cs
index 112109b057c..d399fc5bf0a 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqlServerTest.cs
@@ -65,6 +65,19 @@ FROM [Owner] AS [o]
""");
}
+ public override async Task Update_non_owned_property_on_entity_with_owned_in_join(bool async)
+ {
+ await base.Update_non_owned_property_on_entity_with_owned_in_join(async);
+
+ AssertSql(
+ """
+UPDATE [o]
+SET [o].[Title] = N'NewValue'
+FROM [Owner] AS [o]
+INNER JOIN [Owner] AS [o0] ON [o].[Id] = [o0].[Id]
+""");
+ }
+
public override async Task Update_owned_and_non_owned_properties_with_table_sharing(bool async)
{
await base.Update_owned_and_non_owned_properties_with_table_sharing(async);
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqliteTest.cs
index 3064635eaa4..8925722d997 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqliteTest.cs
@@ -61,6 +61,19 @@ public override async Task Update_non_owned_property_on_entity_with_owned2(bool
""");
}
+ public override async Task Update_non_owned_property_on_entity_with_owned_in_join(bool async)
+ {
+ await base.Update_non_owned_property_on_entity_with_owned_in_join(async);
+
+ AssertSql(
+ """
+UPDATE "Owner" AS "o"
+SET "Title" = 'NewValue'
+FROM "Owner" AS "o0"
+WHERE "o"."Id" = "o0"."Id"
+""");
+ }
+
public override async Task Update_owned_and_non_owned_properties_with_table_sharing(bool async)
{
await base.Update_owned_and_non_owned_properties_with_table_sharing(async);