dotnet/efcore · error · NotImplementedException

Compound assignment of private property not yet supported

Error message

Compound assignment of private property not yet supported

What it means

For non-public properties the generated [UnsafeAccessor] setter handles only simple assignment. A compound assignment (+=, -=, ...) on a private property would need a read through the get accessor and a write through the set accessor as separate operations, which is not implemented, so TranslateNonPublicMemberAssignment throws NotImplementedException when assignmentKind is not SimpleAssignmentExpression.

Source

Thrown at src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs:1736

                : memberExpression.Member,
            forWrite: true);

        // The unsafe accessor declaration has been created; invoke it.
        Result = memberExpression.Member switch
        {
            FieldInfo => AssignmentExpression(
                assignmentKind,
                (ExpressionSyntax)_g.InvocationExpression(
                    _g.IdentifierName(unsafeAccessorDeclaration.Identifier.Text),
                    Translate<ExpressionSyntax>(memberExpression.Expression)),
                Translate<ExpressionSyntax>(value)),

            PropertyInfo =>
                _g.InvocationExpression(
                    _g.IdentifierName(unsafeAccessorDeclaration.Identifier.Text), Translate<ExpressionSyntax>(memberExpression.Expression),
                    assignmentKind is SyntaxKind.SimpleAssignmentExpression
                        ? Translate<ExpressionSyntax>(value)
                        : throw new NotImplementedException("Compound assignment of private property not yet supported")),

            _ => throw new UnreachableException()
        };
    }

    private MethodDeclarationSyntax GetUnsafeAccessorDeclaration(MemberInfo member, bool forWrite = false)
    {
        MethodDeclarationSyntax? unsafeAccessorDeclaration;

        switch (member)
        {
            case FieldInfo field:
            {
                // Note that we generate two accessors for fields (get/set), since the get accessor needs to be used in expression trees,
                // which don't support ref return
                if (_fieldUnsafeAccessors.TryGetValue((field, forWrite), out unsafeAccessorDeclaration))
                {
                    return unsafeAccessorDeclaration;

View on GitHub (pinned to dbf9771522)

Solutions

  1. Expand the compound assignment into an explicit read plus a simple write: privateProp = privateProp + value (simple assignment, value is the sum).
  2. Make the property public so the normal C# compound-assignment path applies.
  3. Perform the accumulation outside the compiled query expression.

Example fix

// before
x.Counter += n;  // AddAssign on a private property -> throws
// after
x.Counter = x.Counter + n;  // simple assignment; read and write each supported
Defensive patterns

Strategy: validation

Validate before calling

// Flag compound assignment on non-public properties
protected override Expression VisitBinary(BinaryExpression b) {
    if (b.NodeType != ExpressionType.Assign && b.Left is MemberExpression m
        && m.Member is System.Reflection.PropertyInfo p
        && !(p.GetMethod?.IsPublic ?? false)) Found = true;
    return b;
}

Prevention

When it happens

Trigger: A lambda performs a compound assignment on a non-public property, e.g. privateProp += value, where privateProp is a property with non-public accessors reached via the unsafe-accessor path.

Common situations: Accumulating into a private/internal property counter inside a compiled expression; using += on a property whose accessors are not public.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/c35755977cede226. Report an issue: GitHub.