{"record":{"id":"964072a6fbd604e1","repo":"dotnet/efcore","slug":"complex-property-complexproperty-cannot-have-b","errorCode":null,"errorMessage":"Complex property '{complexProperty}' cannot have both a JSON column name ('{columnName}') and a JSON property name ('{propertyName}') configured. Use 'ToJson()' to map to a JSON column or 'HasJsonPropertyName()' to map as a JSON property within a containing JSON column, but not both.","messagePattern":"Complex property '(.+?)' cannot have both a JSON column name \\('(.+?)'\\) and a JSON property name \\('(.+?)'\\) configured\\. Use 'ToJson\\(\\)' to map to a JSON column or 'HasJsonPropertyName\\(\\)' to map as a JSON property within a containing JSON column, but not both\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs","lineNumber":286,"sourceCode":"        {\n            throw new InvalidOperationException(\n                RelationalStrings.ComplexCollectionNotMappedToJson(\n                    complexProperty.DeclaringType.DisplayName(), complexProperty.Name));\n        }\n\n        if (!complexProperty.ComplexType.IsMappedToJson()\n            && complexProperty.IsNullable\n            && complexProperty.ComplexType.GetProperties().All(m => m.IsNullable))\n        {\n            throw new InvalidOperationException(\n                RelationalStrings.ComplexPropertyOptionalTableSharing(complexProperty.ComplexType.DisplayName(), complexProperty.Name));\n        }\n\n        if (complexProperty.GetJsonPropertyName() != null)\n        {\n            if (complexProperty.ComplexType.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)?.Value is string columnName)\n            {\n                throw new InvalidOperationException(\n                    RelationalStrings.ComplexPropertyBothJsonColumnAndJsonPropertyName(\n                        $\"{complexProperty.DeclaringType.DisplayName()}.{complexProperty.Name}\",\n                        columnName,\n                        complexProperty.GetJsonPropertyName()));\n            }\n\n            if (!complexProperty.DeclaringType.IsMappedToJson())\n            {\n                throw new InvalidOperationException(\n                    RelationalStrings.ComplexPropertyJsonPropertyNameWithoutJsonMapping(\n                        $\"{complexProperty.DeclaringType.DisplayName()}.{complexProperty.Name}\"));\n            }\n        }\n\n        if (complexProperty.ComplexType.IsMappedToJson())\n        {\n            if (!complexProperty.DeclaringType.IsMappedToJson()\n                && complexProperty.DeclaringType is IComplexType)","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L268-L304","documentation":"ValidatePropertyMapping throws ComplexPropertyBothJsonColumnAndJsonPropertyName when a complex property has both a JSON column mapping (the ContainerColumnName annotation, set by ToJson) and an explicit JSON property name (HasJsonPropertyName). These are mutually exclusive: ToJson makes the complex type its own JSON column, while HasJsonPropertyName designates a property within a containing JSON column. The validator rejects the ambiguous combination.","triggerScenarios":"Chaining .ToJson(\"col\") and .HasJsonPropertyName(\"name\") on the same complex property builder; or scaffolding that produced both annotations. The check reads ContainerColumnName from the annotation and compares with GetJsonPropertyName() result.","commonSituations":"Copy-pasting configuration between owned-type-as-JSON and complex-property-as-JSON-property; partial migration where ToJson was added but HasJsonPropertyName was retained; IDE auto-complete adding the wrong fluent call.","solutions":["Choose one strategy: use ToJson() to map the complex type as its own JSON column, OR use HasJsonPropertyName() to map it inside an existing JSON column — not both.","Remove the conflicting call: drop HasJsonPropertyName when ToJson is present, or drop ToJson when the complex property lives inside another JSON-mapped type.","Audit the configuration chain for stale fluent calls left over from a refactor."],"exampleFix":"// before — both configured\nmodelBuilder.Entity<Order>()\n    .OwnsOne(o => o.Details, d =>\n    {\n        d.ToJson(\"details\");\n        d.HasJsonPropertyName(\"order_details\"); // throws\n    });\n\n// after — pick one (ToJson makes Details its own column)\nmodelBuilder.Entity<Order>()\n    .OwnsOne(o => o.Details, d => d.ToJson(\"details\"));","handlingStrategy":"validation","validationCode":"var hasColumn = complexProperty.ComplexType\n    .FindAnnotation(RelationalAnnotationNames.ContainerColumnName)?.Value is string;\nvar hasJsonName = complexProperty.GetJsonPropertyName() != null;\nif (hasColumn && hasJsonName)\n    throw new InvalidOperationException(\"Complex property cannot have both JSON column and JSON property name.\");","typeGuard":"static bool HasConflictingJsonConfig(IComplexProperty cp)\n    => cp.GetJsonPropertyName() != null\n       && cp.ComplexType.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)?.Value is string;","tryCatchPattern":null,"preventionTips":["Use either ToJson() or HasJsonPropertyName() on a complex property, never both.","Audit fluent chains after refactors for stale configuration calls.","Add a model-validation test asserting no complex property has both annotations."],"tags":["complex-type","json-mapping","configuration-conflict","model-validation"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}