{"record":{"id":"c8ab7b36f918d98e","repo":"dotnet/efcore","slug":"the-complex-collection-property-entitytype-pro","errorCode":null,"errorMessage":"The complex collection property '{entityType}.{property}' must be mapped to a JSON column. Use 'ToJson()' to configure this complex collection as mapped to a JSON column.","messagePattern":"The complex collection property '(.+?)\\.(.+?)' must be mapped to a JSON column\\. Use 'ToJson\\(\\)' to configure this complex collection as mapped to a JSON column\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs","lineNumber":269,"sourceCode":"        if (property is { IsPrimitiveCollection: true }\n            && property.GetTypeMapping().ElementTypeMapping?.ElementTypeMapping != null)\n        {\n            throw new InvalidOperationException(\n                RelationalStrings.NestedCollectionsNotSupported(\n                    property.ClrType.ShortDisplayName(), property.DeclaringType.DisplayName(), property.Name));\n        }\n    }\n\n    /// <inheritdoc />\n    protected override void ValidatePropertyMapping(\n        IComplexProperty complexProperty,\n        IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)\n    {\n        base.ValidatePropertyMapping(complexProperty, logger);\n\n        if (complexProperty.IsCollection && !complexProperty.ComplexType.IsMappedToJson())\n        {\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(","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L251-L287","documentation":"ValidatePropertyMapping (RelationalModelValidator.cs:267-272) throws when a complex property is a collection (complexProperty.IsCollection) but its complex type is not mapped to JSON. Relational providers require complex collections to be stored as a JSON column (there is no table-per-row mapping for an arbitrary complex collection without an entity key), so the model is rejected unless ToJson() is configured.","triggerScenarios":"Declaring OwnsMany(...) / a complex collection property without calling ToJson() on it. Triggered during model validation when the context first builds.","commonSituations":"Adding OwnsMany of a value-object-like complex type assuming it maps to a child table (it does not - owned entity collections map to tables, complex collections must be JSON); forgetting the ToJson() call after introducing a complex collection.","solutions":["Add .ToJson(\"ColumnName\") to the complex collection configuration so it persists as a JSON column.","If you actually want rows in a separate table, use an owned entity collection (OwnsMany with an owned ENTITY type that has identity) instead of a complex type.","Verify the complex type is declared via ComplexType/OwnsMany complex and that ToJson is applied to the collection property."],"exampleFix":"// before\nmodelBuilder.Entity<Order>().OwnsMany(o => o.Tags, t =>\n{\n    t.ComplexProperty(p => p.Style); // Tags is a complex collection not mapped to JSON -> throws\n});\n\n// after\nmodelBuilder.Entity<Order>().OwnsMany(o => o.Tags, t =>\n{\n    t.ToJson(\"TagsJson\");\n    t.ComplexProperty(p => p.Style);\n});","handlingStrategy":"validation","validationCode":"foreach (var et in context.Model.GetEntityTypes())\n{\n    foreach (var cp in et.GetComplexProperties().Where(cp => cp.IsCollection))\n    {\n        if (!cp.ComplexType.IsMappedToJson())\n        {\n            // will throw - add .ToJson(\"col\") to this complex collection.\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair OwnsMany of a complex collection with ToJson().","Use owned ENTITY collections (with identity) when you want a child table instead of JSON.","Add a build-time check that every complex collection is JSON-mapped."],"tags":["complex-collection","json-mapping","model-validation"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}