{"record":{"id":"ed8e622c8fd90983","repo":"dotnet/efcore","slug":"complex-property-complexproperty-is-mapped-to","errorCode":null,"errorMessage":"Complex property '{complexProperty}' is mapped to JSON but its containing type '{containingType}' is not. Map the root complex type to JSON. See https://github.com/dotnet/efcore/issues/36558.","messagePattern":"Complex property '(.+?)' is mapped to JSON but its containing type '(.+?)' is not\\. Map the root complex type to JSON\\. See https://github\\.com/dotnet/efcore/issues/36558\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs","lineNumber":307,"sourceCode":"                        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)\n            {\n                // Issue #36558\n                throw new InvalidOperationException(\n                    RelationalStrings.NestedComplexPropertyJsonWithTableSharing(\n                        $\"{complexProperty.DeclaringType.DisplayName()}.{complexProperty.Name}\",\n                        complexProperty.DeclaringType.DisplayName()));\n            }\n\n            ValidateJsonProperties(complexProperty.ComplexType);\n        }\n    }\n\n    /// <summary>\n    ///     Validates the SQL query mapping for an entity type.\n    /// </summary>\n    /// <param name=\"entityType\">The entity type to validate.</param>\n    /// <param name=\"logger\">The logger to use.</param>\n    protected virtual void ValidateSqlQuery(\n        IEntityType entityType,\n        IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)\n    {","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L289-L325","documentation":"ValidatePropertyMapping (RelationalModelValidator.cs:301-311, tracking issue #36558) throws when a complex property is mapped to JSON but its declaring type is itself a complex type that is NOT mapped to JSON. A JSON column must be rooted at an entity (table); a complex type that flattens to columns cannot host a JSON sub-document. The whole JSON branch must be rooted in JSON, so EF rejects nested JSON inside a table-shared complex type.","triggerScenarios":"A nested complex property (declaring type is a complex type) configured with ToJson() while the outer complex type is mapped to columns (no ToJson on the root). Thrown at model validation.","commonSituations":"Marking only an inner complex property as JSON during a partial migration to JSON columns; building nested value-object structures and applying ToJson at the wrong level.","solutions":["Move the ToJson() up to the root complex property so the entire JSON subtree (entity root -> complex -> nested complex) is consistently JSON-mapped.","If the outer complex must stay table-mapped, map the inner collection/entity to its own table or as owned entities rather than a nested JSON column.","Restructure so only the entity-rooted complex type carries ToJson()."],"exampleFix":"// before - JSON nested inside a table-shared complex type\nmodelBuilder.Entity<Customer>().ComplexProperty(c => c.Contact, contact =>\n{\n    // Contact is flattened to columns (no ToJson)\n    contact.OwnsOne(c => c.Preferences, prefs =>\n    {\n        prefs.ToJson(\"PrefsJson\"); // throws: parent not JSON\n    });\n});\n\n// after - root the whole JSON subtree at the entity\nmodelBuilder.Entity<Customer>().ComplexProperty(c => c.Contact, contact =>\n{\n    contact.ToJson(\"ContactJson\");\n    contact.OwnsOne(c => c.Preferences, prefs => { });\n});","handlingStrategy":"validation","validationCode":"foreach (var et in context.Model.GetEntityTypes())\n{\n    foreach (var cp in et.GetComplexProperties())\n    {\n        if (cp.ComplexType.IsMappedToJson()\n            && !cp.DeclaringType.IsMappedToJson()\n            && cp.DeclaringType is IComplexType)\n        {\n            // will throw (#36558) - move ToJson() to the root complex property.\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Root JSON columns at the entity, never at a nested complex type inside a table-shared one.","When introducing ToJson(), apply it from the top of the subtree downward.","Treat issue #36558 as the canonical reference for this constraint."],"tags":["json-mapping","complex-type","nested","model-validation"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}