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
Thrown by LinqToCSharpSyntaxTranslator.TranslateNonPublicMemberAssignment when the lvalue is a private/internal PropertyInfo (accessed via UnsafeAccessor) and the assignment is not a simple SyntaxKind.SimpleAssignmentExpression — i.e. a compound assignment such as +=, -=, *=, etc. The property path only supports direct setter invocation; compound assignments would require read-modify-write across getter and setter, which is not implemented.
Solutions
- Rewrite the compound assignment as an explicit read-then-write: read via the getter, combine with the right operand, then assign via the setter using simple assignment.
- Expose a public method that performs the compound operation and call it instead.
- Make the property public so the normal assignment path applies (where compound operators are supported).
Example fix
// before — compound assign to a private property
var prop = typeof(Item).GetProperty("Count",
BindingFlags.NonPublic | BindingFlags.Instance)!;
var assign = Expression.AddAssign(
Expression.Property(itemParam, prop),
Expression.Constant(1));
// after — expand to getter + setter with simple assignment
var read = Expression.Property(itemParam, prop);
var assign = Expression.Assign(
Expression.Property(itemParam, prop),
Expression.Add(read, Expression.Constant(1))); Defensive patterns
Strategy: validation
Validate before calling
// Detect compound assignments targeting non-public properties.
using System.Linq.Expressions;
using System.Reflection;
static readonly HashSet<ExpressionType> CompoundAssignTypes = new()
{
ExpressionType.AddAssign, ExpressionType.AddAssignChecked,
ExpressionType.SubtractAssign, ExpressionType.SubtractAssignChecked,
ExpressionType.MultiplyAssign, ExpressionType.MultiplyAssignChecked,
ExpressionType.DivideAssign, ExpressionType.ModuloAssign,
ExpressionType.AndAssign, ExpressionType.OrAssign,
ExpressionType.LeftShiftAssign, ExpressionType.RightShiftAssign,
ExpressionType.ExclusiveOrAssign, ExpressionType.PowerAssign,
};
bool HasCompoundAssignOnPrivateProperty(Expression e)
{
bool bad = false;
new CompoundChecker(b =>
{
if (CompoundAssignTypes.Contains(b.NodeType)
&& b.Left is MemberExpression { Member: PropertyInfo pi }
&& (pi.SetMethod?.IsPublic ?? false) == false)
bad = true;
}).Visit(e);
return bad;
}
class CompoundChecker(Action<BinaryExpression> onBinary) : ExpressionVisitor
{
protected override Expression VisitBinary(BinaryExpression b) { onBinary(b); return base.VisitBinary(b); }
} Prevention
- Expand compound assignments on private properties into getter + binary op + setter with simple assignment.
- Expose a public method that performs the compound operation and call it from the tree.
- Pre-scan trees for compound assignments whose lvalues are non-public properties.
When it happens
Trigger: An expression tree performs a compound assignment (AddAssign, SubtractAssign, MultiplyAssign, ...) whose left side is a MemberExpression over a non-public property, reaching TranslateNonPublicMemberAssignment with assignmentKind != SimpleAssignmentExpression.
Common situations: Reflection-built expression trees that mutate private property state incrementally; provider/test code that does Count++ on a private property; converting C# compound-assignment code into expression trees that target private state.
Related errors
- Private static field access
- Private static field assignment
- Anonymous type creation without members
- Can't translate method
- BinaryExpression with
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/c35755977cede226.
Report an issue: GitHub.
Appendix: 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 3a2006ef56)