{"record":{"id":"4d823e408cb309d9","repo":"DapperLib/Dapper","slug":"expression-must-be-a-property-field-chain-off-of-a","errorCode":null,"errorMessage":"Expression must be a property/field chain off of a(n) {typeof(T).Name} instance","messagePattern":"Expression must be a property/field chain off of a\\(n\\) (.+?) instance","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"Dapper/DynamicParameters.cs","lineNumber":335,"sourceCode":"                return default!;\r\n            }\r\n            return (T)val!;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Allows you to automatically populate a target property/field from output parameters. It actually\r\n        /// creates an InputOutput parameter, so you can still pass data in.\r\n        /// </summary>\r\n        /// <typeparam name=\"T\"></typeparam>\r\n        /// <param name=\"target\">The object whose property/field you wish to populate.</param>\r\n        /// <param name=\"expression\">A MemberExpression targeting a property/field of the target (or descendant thereof.)</param>\r\n        /// <param name=\"dbType\"></param>\r\n        /// <param name=\"size\">The size to set on the parameter. Defaults to 0, or DbString.DefaultLength in case of strings.</param>\r\n        /// <returns>The DynamicParameters instance</returns>\r\n        public DynamicParameters Output<T>(T target, Expression<Func<T, object?>> expression, DbType? dbType = null, int? size = null)\r\n        {\r\n            static void ThrowInvalidChain()\r\n                => throw new InvalidOperationException($\"Expression must be a property/field chain off of a(n) {typeof(T).Name} instance\");\r\n\r\n            // Is it even a MemberExpression?\r\n#pragma warning disable IDE0019 // Use pattern matching - already complex enough\r\n            var lastMemberAccess = expression.Body as MemberExpression;\r\n#pragma warning restore IDE0019 // Use pattern matching\r\n\r\n            if (lastMemberAccess is null\r\n                || (!(lastMemberAccess.Member is PropertyInfo)\r\n                    && !(lastMemberAccess.Member is FieldInfo)))\r\n            {\r\n                if (expression.Body.NodeType == ExpressionType.Convert\r\n                    && expression.Body.Type == typeof(object)\r\n                    && ((UnaryExpression)expression.Body).Operand is MemberExpression member)\r\n                {\r\n                    // It's got to be unboxed\r\n                    lastMemberAccess = member;\r\n                }\r\n                else\r","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/DapperLib/Dapper/blob/72a54c475f75e18cb93cba0809d00a5e6e49efd9/Dapper/DynamicParameters.cs#L317-L353","documentation":"DynamicParameters.Output<T> throws InvalidOperationException(\"Expression must be a property/field chain off of a(n) {T} instance\") when the supplied lambda body is not a chain of MemberExpressions (property/field access) rooted at a ParameterExpression of type T. The method walks the expression to derive parameter names from the member chain (e.g. Post.Author.Name → @PostAuthorName) and to wire output values back; an invalid expression cannot be deconstructed. A single-level Convert-to-object unwrap is tolerated, but anything else (method calls, indexing, constants) is rejected.","triggerScenarios":"Passing a lambda that calls a method or indexes a collection (x => x.Items[0].Id); passing an expression that does not start from the target T (e.g. capturing an external variable); passing a constant or a computed expression rather than a pure member chain.","commonSituations":"Trying to output-bind into a nested collection element; refactoring a property into a method and forgetting to update the Output call; copy-pasting an expression that referenced a different root type.","solutions":["Ensure the lambda is a pure property/field chain rooted at the target (target => target.Sub.FinalField).","For nested members, only property/field traversal is supported — flatten the target or expose an intermediate property.","Avoid method calls, indexers, arithmetic, or captured variables inside the Output expression."],"exampleFix":"// before\np.Output(obj, x => x.Items[0].Id);\n\n// after\np.Output(obj, x => x.PrimaryItemId);","handlingStrategy":"validation","validationCode":"// ensure the lambda is a pure member chain: target.Sub.Field\np.Output(target, x => x.Sub.Field);","typeGuard":"static bool IsMemberChain<T>(Expression<Func<T, object?>> e)\n{\n    Expression b = e.Body;\n    if (b is UnaryExpression u && b.NodeType == ExpressionType.Convert) b = u.Operand;\n    while (b is MemberExpression) b = ((MemberExpression)b).Expression!;\n    return b is ParameterExpression;\n}","tryCatchPattern":"try { p.Output(target, expr); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"property/field chain\"))\n{ /* rewrite the expression as a pure member chain */ }","preventionTips":["Only pass property/field access chains to Output; no methods, indexers, or captured vars.","Unit-test Output expressions against the helper guard above."],"tags":["dynamic-parameters","expression-trees","output-parameters"],"backgroundTag":null,"analyzedSha":"72a54c475f75e18cb93cba0809d00a5e6e49efd9","analyzedAt":"2026-08-13T14:18:18.115Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}