{"record":{"id":"035e83139579876a","repo":"AvaloniaUI/Avalonia","slug":"invalid-method-call-in-binding-expression-node","errorCode":null,"errorMessage":"Invalid method call in binding expression: '{node.Method.DeclaringType}.{node.Method.Name}'.","messagePattern":"Invalid method call in binding expression: '(.+?)\\.(.+?)'\\.","errorType":"validation","errorClass":"ExpressionParseException","httpStatus":null,"severity":"error","filePath":"src/Avalonia.Base/Data/Core/Parsers/BindingExpressionVisitor.cs","lineNumber":165,"sourceCode":"            {\n                return Add(instance, node, x => x.StreamTask());\n            }\n            else if (instanceType is not null && ObservableStreamPlugin.MatchesType(instanceType))\n            {\n                return Add(instance, node, x => x.StreamObservable());\n            }\n        }\n        else if (method == BindingExpressionVisitorMembers.CreateDelegateMethod)\n        {\n            var methodInfo = GetValue<MethodInfo>(node.Object!);\n            var delegateType = GetValue<Type>(node.Arguments[0]);\n            return Add(node.Arguments[1], node, x => x.Method(\n                methodInfo.MethodHandle,\n                delegateType.TypeHandle,\n                acceptsNull: false));\n        }\n\n        throw new ExpressionParseException(0, $\"Invalid method call in binding expression: '{node.Method.DeclaringType}.{node.Method.Name}'.\");\n    }\n\n    protected override Expression VisitParameter(ParameterExpression node)\n    {\n        if (node == _rootExpression.Parameters[0] && _head is null)\n            _head = node;\n        return base.VisitParameter(node);\n    }\n    \n    protected override Expression VisitUnary(UnaryExpression node)\n    {\n        if (node.NodeType == ExpressionType.Not && node.Type == typeof(bool))\n        {\n            return Add(node.Operand, node, x => x.Not());\n        }\n        else if (node.NodeType == ExpressionType.Convert)\n        {\n            // Allow reference type casts (both upcasts and downcasts) but reject value type conversions","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/AvaloniaUI/Avalonia/blob/11c542726898ae954a1ef668c65ec79ec92ab17d/src/Avalonia.Base/Data/Core/Parsers/BindingExpressionVisitor.cs#L147-L183","documentation":"Thrown by BindingExpressionVisitor.VisitMethodCall when a method invocation in the compiled binding lambda does not match any of the recognized method patterns: (1) get_Item (indexer getter), (2) Get (multi-dimensional array getter), (3) StreamBinding extension methods, or (4) ReflectionExtensions.CreateDelegate. Any other method call is rejected because compiled bindings represent a property path, not arbitrary code execution.","triggerScenarios":"Calling a method in the binding lambda such as x => x.Items.Count() (LINQ method), x => x.Name.ToString(), x => x.Value.ToString(CultureInfo), or any instance/static method call. The C# compiler happily compiles these, but the visitor cannot translate them into a CompiledBindingPath.","commonSituations":"Using LINQ methods like Count(), First(), Select() in a compiled binding; calling ToString() or other formatting methods; expecting compiled bindings to support method chaining like a fluent API; migrating from code-behind property access to bindings without adjusting the expression.","solutions":["Move the method call into a property on the view model: 'public int ItemCount => Items.Count();' and bind to ItemCount.","For formatting, use the binding's StringFormat converter instead of calling ToString() in the lambda.","For stream bindings, use the dedicated '^' stream operator or the StreamBinding() extension method which IS supported."],"exampleFix":"// before: method call in compiled binding\n<TextBlock Text=\"{CompiledBinding Items.Count()}\" />\n\n// after: expose as property\n<TextBlock Text=\"{CompiledBinding ItemCount}\" />\n// view model:\npublic int ItemCount => Items.Count;","handlingStrategy":"validation","validationCode":"// Detect method calls in a binding lambda that are not recognized stream/indexer patterns\nstatic bool HasUnsupportedMethodCall<TIn, TOut>(Expression<Func<TIn, TOut>> expr)\n{\n    var allowedMethods = new HashSet<string> { \"get_Item\", \"Get\" };\n    bool found = false;\n    expr.Body.Visit(b =>\n    {\n        if (b is MethodCallExpression mc\n            && !allowedMethods.Contains(mc.Method.Name)\n            && mc.Method.DeclaringType != typeof(StreamBindingExtensions))\n            found = true;\n    });\n    return found;\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    var path = BindingExpressionVisitor<TViewModel>.BuildPath<TProp>(expr);\n}\ncatch (ExpressionParseException ex) when (ex.Message.Contains(\"Invalid method call\"))\n{\n    logger.LogError($\"Compiled binding contains a method call that is not a recognized indexer or stream operation: {ex.Message}\");\n}","preventionTips":["Never call methods in a compiled binding lambda — only property access, indexing, casts, and !.","Move method results (Count, ToString, etc.) into view-model properties.","Use IValueConverter for transformations that must happen at binding time."],"tags":["avalonia","binding","compiled-binding","expression-tree","method-call","visitor","unsupported-operation"],"backgroundTag":null,"analyzedSha":"11c542726898ae954a1ef668c65ec79ec92ab17d","analyzedAt":"2026-08-13T11:57:40.261Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}