dotnet/efcore · error · NotImplementedException

Unary node with non-null method

Error message

Unary node with non-null method

What it means

Thrown by VisitUnary when a UnaryExpression has a non-null Method that is not a conversion operator (op_Implicit/op_Explicit) and is neither HideBySig nor SpecialName. The translator only knows how to render built-in unary operators and user-defined conversions; any other method-backed unary operation (e.g. a custom overloaded unary operator that does not fit the recognised shapes) is unimplemented and throws NotImplementedException.

Solutions

  1. Avoid using custom method-backed unary operators in expressions handed to the C# translator.
  2. Replace the operator call with an equivalent built-in unary expression or a method call expression.
  3. If it is a conversion, ensure the method is named op_Implicit or op_Explicit so it is recognised.

Example fix

// before: custom unary operator with Method set to a non-conversion MethodInfo
// after: express the operation as a method call or built-in unary node without a custom Method
Defensive patterns

Strategy: validation

Validate before calling

// Reject unary nodes with unsupported backing methods
static bool HasUnsupportedUnaryMethod(Expression e)
{
    bool bad = false;
    ExpressionVisitor visitor = null!;
    visitor = new ExpressionVisitorWrapper {
        OnUnary = u => { if (u.Method is not null && u.Method.Name is not ("op_Implicit" or "op_Explicit") && !u.Method.IsSpecialName) bad = true; }
    };
    visitor.Visit(e);
    return bad;
}

Type guard

static bool IsTranslatableUnary(UnaryExpression u) => u.Method is null || u.Method.Name is "op_Implicit" or "op_Explicit" || u.Method.IsSpecialName || u.Method.IsHideBySig;

Try / catch

try { var csharp = translator.Translate(expr); }
catch (NotImplementedException ex) when (ex.Message.Contains("Unary node with non-null method"))
{ throw new InvalidOperationException("A unary node carries an unsupported operator method.", ex); }

Prevention

When it happens

Trigger: An expression tree contains a UnaryExpression whose Method is a custom operator MethodInfo that is not op_Implicit/op_Explicit, not HideBySig, and not SpecialName, and the tree is translated to C#.

Common situations: User-defined unary operators surfaced as method-backed UnaryExpressions; expression trees built by tools that attach arbitrary MethodInfo objects to unary nodes; rare custom operator overloads in value converters or computed selectors.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/bc1895abfefcd721. Report an issue: GitHub.

Appendix: source

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

            ExpressionType.TypeEqual
                => BinaryExpression(SyntaxKind.EqualsExpression, visitedExpression, TypeOfExpression(Generate(node.TypeOperand))),

            _ => throw new ArgumentOutOfRangeException()
        };

        return node;
    }

    /// <inheritdoc />
    protected override Expression VisitUnary(UnaryExpression unary)
    {
        if (unary.Method is not null
            && !unary.Method.IsHideBySig
            && !unary.Method.IsSpecialName
            && unary.Method.Name != "op_Implicit"
            && unary.Method.Name != "op_Explicit")
        {
            throw new NotImplementedException("Unary node with non-null method");
        }

        using var _ = ChangeContext(ExpressionContext.Expression);

        var operand = Translate<ExpressionSyntax>(unary.Operand);

        // TODO: Confirm what to do with the checked expression types

        Result = unary.NodeType switch
        {
            ExpressionType.Negate => _g.NegateExpression(operand),
            ExpressionType.NegateChecked => _g.NegateExpression(operand),
            ExpressionType.Not when unary.Type == typeof(bool) => _g.LogicalNotExpression(operand),
            ExpressionType.Not => _g.BitwiseNotExpression(operand),
            ExpressionType.OnesComplement => _g.BitwiseNotExpression(operand),
            ExpressionType.IsFalse => _g.LogicalNotExpression(operand),
            ExpressionType.IsTrue => operand,
            ExpressionType.ArrayLength => _g.MemberAccessExpression(operand, "Length"),

View on GitHub (pinned to 3a2006ef56)