{"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/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L251-L287","documentation":"ValidatePropertyMapping throws ComplexCollectionNotMappedToJson when a complex property is a collection (complexProperty.IsCollection) but its ComplexType is not mapped to JSON. Relational providers require complex collections to live in a JSON column because there is no general multi-row flattening for complex types; only JSON carries the array semantics.","triggerScenarios":"Calling OwnsMany (or a complex collection property) without ToJson on the owning configuration. The check is `complexProperty.IsCollection && !complexProperty.ComplexType.IsMappedToJson()`.","commonSituations":"Migrating from owned collection (one-to-many) to complex collection and forgetting ToJson; scaffolding a complex collection then customizing it without JSON; assuming OwnsMany maps to a child table like before EF9 complex types.","solutions":["Add .ToJson(\"column_name\") to the OwnsMany configuration for the complex collection.","If you actually want relational one-to-many with a child table, use OwnsMany with a navigation that the model treats as an entity collection (HasOne/HasMany) instead of a complex collection.","Verify the property is genuinely a complex collection (List<ComplexType>) and not an entity collection before applying ToJson."],"exampleFix":"// before\nmodelBuilder.Entity<Order>()\n    .OwnsMany(o => o.Items, i =>\n    {\n        i.Property(p => p.Sku).HasMaxLength(50);\n    }); // throws — no ToJson\n\n// after\nmodelBuilder.Entity<Order>()\n    .OwnsMany(o => o.Items, i =>\n    {\n        i.ToJson(\"items\");\n        i.Property(p => p.Sku).HasMaxLength(50);\n    });","handlingStrategy":"validation","validationCode":"if (complexProperty.IsCollection && !complexProperty.ComplexType.IsMappedToJson())\n    throw new InvalidOperationException(\"Complex collection must be mapped to JSON; add ToJson().\");","typeGuard":"static bool IsComplexCollectionMappedToJson(IComplexProperty cp)\n    => !cp.IsCollection || cp.ComplexType.IsMappedToJson();","tryCatchPattern":null,"preventionTips":["Always chain .ToJson() when configuring OwnsMany for complex collections.","Distinguish complex collections (need JSON) from entity one-to-many (child table).","Add a model-finalization unit test to catch missing ToJson early."],"tags":["complex-type","complex-collection","json-mapping","model-validation"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}