@@ -1029,7 +1029,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
10291029 {
10301030 if ( source . ShaperExpression is IncludeExpression includeExpression )
10311031 {
1032- source = source . UpdateShaperExpression ( PruneOwnedIncludes ( includeExpression ) ) ;
1032+ source = source . UpdateShaperExpression ( PruneIncludes ( includeExpression ) ) ;
10331033 }
10341034
10351035 if ( source . ShaperExpression is not EntityShaperExpression entityShaperExpression )
@@ -1128,20 +1128,6 @@ static bool AreOtherNonOwnedEntityTypesInTheTable(IEntityType rootType, ITableBa
11281128 Expression . Quote ( Expression . Lambda ( predicateBody , entityParameter ) ) ) ;
11291129
11301130 return TranslateExecuteDelete ( ( ShapedQueryExpression ) Visit ( newSource ) ) ;
1131-
1132- static Expression PruneOwnedIncludes ( IncludeExpression includeExpression )
1133- {
1134- if ( includeExpression . Navigation is ISkipNavigation
1135- || includeExpression . Navigation is not INavigation navigation
1136- || ! navigation . ForeignKey . IsOwnership )
1137- {
1138- return includeExpression ;
1139- }
1140-
1141- return includeExpression . EntityExpression is IncludeExpression innerIncludeExpression
1142- ? PruneOwnedIncludes ( innerIncludeExpression )
1143- : includeExpression . EntityExpression ;
1144- }
11451131 }
11461132
11471133 /// <summary>
@@ -1163,6 +1149,13 @@ static Expression PruneOwnedIncludes(IncludeExpression includeExpression)
11631149 ShapedQueryExpression source ,
11641150 LambdaExpression setPropertyCalls )
11651151 {
1152+ // Our source may have IncludeExpressions because of owned entities or auto-include; unwrap these, as they're meaningless for
1153+ // ExecuteUpdate's lambdas. Note that we don't currently support updates across tables.
1154+ if ( source . ShaperExpression is IncludeExpression includeExpression )
1155+ {
1156+ source = source . UpdateShaperExpression ( PruneIncludes ( includeExpression ) ) ;
1157+ }
1158+
11661159 var propertyValueLambdaExpressions = new List < ( LambdaExpression , Expression ) > ( ) ;
11671160 PopulateSetPropertyCalls ( setPropertyCalls . Body , propertyValueLambdaExpressions , setPropertyCalls . Parameters [ 0 ] ) ;
11681161 if ( TranslationErrorDetails != null )
@@ -1381,11 +1374,16 @@ void PopulateSetPropertyCalls(
13811374 when parameter == p :
13821375 break ;
13831376
1384- case MethodCallExpression methodCallExpression
1385- when methodCallExpression . Method . IsGenericMethod
1386- && methodCallExpression . Method . Name == nameof ( SetPropertyCalls < int > . SetProperty )
1387- && methodCallExpression . Method . DeclaringType ! . IsGenericType
1388- && methodCallExpression . Method . DeclaringType . GetGenericTypeDefinition ( ) == typeof ( SetPropertyCalls < > ) :
1377+ case MethodCallExpression
1378+ {
1379+ Method :
1380+ {
1381+ IsGenericMethod : true ,
1382+ Name : nameof ( SetPropertyCalls < int > . SetProperty ) ,
1383+ DeclaringType . IsGenericType : true
1384+ }
1385+ } methodCallExpression
1386+ when methodCallExpression . Method . DeclaringType . GetGenericTypeDefinition ( ) == typeof ( SetPropertyCalls < > ) :
13891387
13901388 list . Add ( ( ( LambdaExpression ) methodCallExpression . Arguments [ 0 ] , methodCallExpression . Arguments [ 1 ] ) ) ;
13911389
@@ -1400,8 +1398,8 @@ when methodCallExpression.Method.IsGenericMethod
14001398 }
14011399
14021400 // For property setter selectors in ExecuteUpdate, we support only simple member access, EF.Function, etc.
1403- // We also unwrap casts to interface/base class (#29618), as well as IncludeExpressions (which occur when the target entity has
1404- // owned entities, #28727).
1401+ // We also unwrap casts to interface/base class (#29618). Note that owned IncludeExpressions have already been pruned from the
1402+ // source before remapping the lambda ( #28727).
14051403 static bool TryProcessPropertyAccess (
14061404 IModel model ,
14071405 ref Expression expression ,
@@ -1410,7 +1408,7 @@ static bool TryProcessPropertyAccess(
14101408 expression = expression . UnwrapTypeConversion ( out _ ) ;
14111409
14121410 if ( expression is MemberExpression { Expression : not null } memberExpression
1413- && Unwrap ( memberExpression . Expression ) is EntityShaperExpression ese )
1411+ && memberExpression . Expression . UnwrapTypeConversion ( out _ ) is EntityShaperExpression ese )
14141412 {
14151413 expression = memberExpression . Update ( ese ) ;
14161414 entityShaperExpression = ese ;
@@ -1420,7 +1418,7 @@ static bool TryProcessPropertyAccess(
14201418 if ( expression is MethodCallExpression mce )
14211419 {
14221420 if ( mce . TryGetEFPropertyArguments ( out var source , out _ )
1423- && Unwrap ( source ) is EntityShaperExpression ese1 )
1421+ && source . UnwrapTypeConversion ( out _ ) is EntityShaperExpression ese1 )
14241422 {
14251423 if ( source != ese1 )
14261424 {
@@ -1434,7 +1432,7 @@ static bool TryProcessPropertyAccess(
14341432 }
14351433
14361434 if ( mce . TryGetIndexerArguments ( model , out var source2 , out _ )
1437- && Unwrap ( source2 ) is EntityShaperExpression ese2 )
1435+ && source2 . UnwrapTypeConversion ( out _ ) is EntityShaperExpression ese2 )
14381436 {
14391437 expression = mce . Update ( ese2 , mce . Arguments ) ;
14401438 entityShaperExpression = ese2 ;
@@ -1444,18 +1442,6 @@ static bool TryProcessPropertyAccess(
14441442
14451443 entityShaperExpression = null ;
14461444 return false ;
1447-
1448- static Expression Unwrap ( Expression expression )
1449- {
1450- expression = expression . UnwrapTypeConversion ( out _ ) ;
1451-
1452- while ( expression is IncludeExpression includeExpression )
1453- {
1454- expression = includeExpression . EntityExpression ;
1455- }
1456-
1457- return expression ;
1458- }
14591445 }
14601446
14611447 // Old quirked implementation only
@@ -1660,6 +1646,18 @@ private Expression RemapLambdaBody(ShapedQueryExpression shapedQueryExpression,
16601646 private Expression ExpandSharedTypeEntities ( SelectExpression selectExpression , Expression lambdaBody )
16611647 => _sharedTypeEntityExpandingExpressionVisitor . Expand ( selectExpression , lambdaBody ) ;
16621648
1649+ private static Expression PruneIncludes ( IncludeExpression includeExpression )
1650+ {
1651+ if ( includeExpression . Navigation is ISkipNavigation or not INavigation )
1652+ {
1653+ return includeExpression ;
1654+ }
1655+
1656+ return includeExpression . EntityExpression is IncludeExpression innerIncludeExpression
1657+ ? PruneIncludes ( innerIncludeExpression )
1658+ : includeExpression . EntityExpression ;
1659+ }
1660+
16631661 private sealed class SharedTypeEntityExpandingExpressionVisitor : ExpressionVisitor
16641662 {
16651663 private readonly RelationalSqlTranslatingExpressionVisitor _sqlTranslator ;
0 commit comments