Skip to content

Fix SetProperty discard lambda failing for nullable value type properties in ExecuteUpdate#37975

Merged
roji merged 2 commits intomainfrom
copilot/fix-setproperty-lambda-error
Mar 24, 2026
Merged

Fix SetProperty discard lambda failing for nullable value type properties in ExecuteUpdate#37975
roji merged 2 commits intomainfrom
copilot/fix-setproperty-lambda-error

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 23, 2026

In EF Core 10+, SetProperty(p => p.NullableIntProp, _ => 1) throws InvalidOperationException: No coercion operator is defined between types 'Func<Entity, int?>' and 'int'. Non-nullable and reference types are unaffected.

// Throws in EF Core 10+
await ctx.Orders.ExecuteUpdateAsync(s =>
    s.SetProperty(p => p.UpdatedById, _ => SystemUserId)); // UpdatedById is int?

Root cause

ExpressionTreeFuncletizer.ProcessEvaluatableRoot, when constantizing a node, sets state = NoEvaluatability then calls ConvertIfNeeded(Constant(1, int), int?) to wrap the result. ConvertIfNeeded calls Visit(Convert(Constant(1, int), int?)) — which resets and re-computes _state as EvaluatableWithoutCapturedVariable, since the Convert node is itself evaluatable.

Because state is ref _state, this corrupts the state back in VisitLambda: the lambda _ => 1 incorrectly appears evaluatable, so its parent (VisitNew for the setter NewExpression) compiles the whole lambda to a Func<Entity, int?> and emits a QueryParameterExpression<Func<Entity, int?>>. TranslateSetterValueSelector then receives this parameter expression instead of a LambdaExpression and throws.

Fix

  • ExpressionTreeFuncletizer.ProcessEvaluatableRoot: After ConvertIfNeeded returns in the constant-evaluation path, reassign state = State.NoEvaluatability to undo the corruption introduced by ConvertIfNeeded's internal Visit call.

Test

  • Added Update_Where_set_nullable_int_constant_via_discard_lambda to NorthwindBulkUpdatesTestBase using Product.SupplierID (int?) with _ => 1, with SQL baseline assertions in the SQLite and SQL Server test classes.
Original prompt

This section details on the original issue you should resolve

<issue_title>Breaking change in EF Core 10: SetProperty lambda with discarded parameter (_ =>) fails with "No coercion operator defined" for nullable int properties in ExecuteUpdateAsync</issue_title>
<issue_description>
After upgrading from EF Core 9 to EF Core 10, ExecuteUpdateAsync throws a System.InvalidOperationException when using SetProperty to assign a constant or captured value to a nullable property (int?).

I'm aware of the [breaking change in EF Core 10](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/breaking-changes#execute-update-expression) where the column setters parameter moved from Expression<Func<...>> to Func<...>. However, the nullable value-type coercion failure seems like it may be an unintended side effect of that change rather than expected behavior.

Exception

System.InvalidOperationException: No coercion operator is defined between types
'System.Func`2[MyApp.Models.MyEntity,System.Nullable`1[System.Int32]]' and 'System.Int32'.

Reproduce

Entity

public class Order
{
    public int Id { get; set; }
    public int? UpdatedById { get; set; }
    public string Status { get; set; }
}

Failing code (worked in EF Core 9)

const int SystemUserId = 1;

await dbContext.Orders
    .Where(x => ids.Contains(x.Id))
    .ExecuteUpdateAsync(s =>
    {
        s.SetProperty(p => p.UpdatedById, _ => SystemUserId);  // 💥 throws
        s.SetProperty(p => p.Status, _ => "Expired");
    });

The same pattern with non-nullable properties or string properties does not fail.

Workaround 1 — use entity parameter instead of discard

s.SetProperty(p => p.UpdatedById, p => (int?)SystemUserId);

Workaround 2 — capture as a pre-typed variable

int? systemUserId = SystemUserId;

await dbContext.Orders
    .Where(x => ids.Contains(x.Id))
    .ExecuteUpdateAsync(s =>
    {
        s.SetProperty(p => p.UpdatedById, p => systemUserId);
        s.SetProperty(p => p.Status, _ => "Expired");
    });

Expected behavior

Assigning a constant int value to an int? property via SetProperty should work regardless of whether the lambda uses a discard (_ =>) or an entity parameter (p =>). The implicit intint? coercion should be handled transparently, as it was in EF Core 9.

Actual behavior

EF Core 10 throws InvalidOperationException at query compilation time. The error originates in TryTranslateSetterValueSelector, suggesting the new Func-based setter pipeline does not handle the intNullable<int> coercion when the lambda uses a discarded parameter.

Notably:

  • Switching from _ => to p => resolves the issue, even though the parameter is unused.
  • Pre-casting the value to int? outside the lambda also resolves it.
  • Non-nullable value types and reference types (string) are unaffected.

This suggests the regression is specific to how the discard-parameter lambda's return type is inferred for nullable value types under the new Func-based API.

Stack trace

at System.Linq.Expressions.Expression.GetUserDefinedCoercionOrThrow(ExpressionType coercionType, Expression expression, Type convertToType)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor
    .<>c__DisplayClass108_0.<TryTranslateSetters>g__TryTranslateSetterValueSelector|6(...)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor
    .<>c__DisplayClass108_0.<TryTranslateScalarSetterValueSelector|5(...)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor
    .TryTranslateSetters(...)
at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal
    .SqlServerQueryableMethodTranslatingExpressionVisitor.TryTranslateSetters(...)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor
    .TranslateExecuteUpdate(...)

Question

Is the nullable coercion failure with _ => an intended consequence of the Expression<Func<...>>Func<...> migration, or is this a regression that should be handled by the new setter translation pipeline?

Provider and version information

  • EF Core version: 10.0.0
  • Database provider: Microsoft.EntityFrameworkCore.SqlServer
  • Target framework: .NET 10
  • Operating system: Linux (Docker)
  • IDE: Visual Studio / Rider</issue_description>

Comments on the Issue (you are @copilot in this section)

@roji # AI Triage

The below is an AI-generated analysis and may contain inaccuracies.

Classification

Bug — confirmed regression from EF Core 9 to EF Core 10. The bug also exists on the current main branch (11.0 development).

Root cause

The bug is in `...


⚡ Quickly spin up Copilot coding agent tasks from anywhere on your macOS or Windows machine with Raycast.

Copilot AI changed the title [WIP] Fix SetProperty lambda issue for nullable int properties Fix SetProperty discard lambda failing for nullable value type properties in ExecuteUpdate Mar 23, 2026
Copilot AI requested a review from roji March 23, 2026 20:13
Copilot finished work on behalf of roji March 23, 2026 20:13
@roji roji marked this pull request as ready for review March 23, 2026 20:19
@roji roji requested a review from a team as a code owner March 23, 2026 20:19
Copilot AI review requested due to automatic review settings March 23, 2026 20:19
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a regression in EF Core 10+ where ExecuteUpdate/SetProperty with a discard lambda (e.g. _ => 1) fails for nullable value type properties (e.g. int?) due to ExpressionTreeFuncletizer state being inadvertently re-marked as evaluatable during constantization.

Changes:

  • Reset funcletizer state back to NoEvaluatability after ConvertIfNeeded(...) in the constant-evaluation path to prevent state corruption.
  • Add a specification test covering int? property assignment via discard lambda in ExecuteUpdate.
  • Add SQL baselines for the new test in SQLite and SQL Server functional tests.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs Resets state after ConvertIfNeeded to avoid incorrectly treating a discard setter lambda as evaluatable.
test/EFCore.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs Adds spec test for setting Product.SupplierID (int?) via _ => 1 in ExecuteUpdate.
test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs Adds SQLite SQL baseline for the new nullable int discard-lambda update test.
test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs Adds SQL Server SQL baseline for the new nullable int discard-lambda update test.

@roji roji enabled auto-merge (squash) March 23, 2026 20:22
@roji roji merged commit 97db209 into main Mar 24, 2026
16 checks passed
@roji roji deleted the copilot/fix-setproperty-lambda-error branch March 24, 2026 01:15
roji added a commit to roji/efcore that referenced this pull request Mar 26, 2026
…ties in ExecuteUpdate

Backport of dotnet#37975 to release/10.0.

In ExpressionTreeFuncletizer.ProcessEvaluatableRoot, ConvertIfNeeded calls Visit
which corrupts _state back to EvaluatableWithoutCapturedVariable when wrapping a
constant int to int?. This causes the parent VisitLambda to treat the lambda as
evaluatable, compiling it to a Func and breaking TranslateSetterValueSelector.

Fix: reset state to NoEvaluatability after ConvertIfNeeded returns.
Added quirk Microsoft.EntityFrameworkCore.Issue37974 to opt out.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Regression: ExecuteUpdate fails for constant/literals assigned to for nullable int properties

4 participants