dotnet/efcore · error · InvalidOperationException

Complex property '{complexProperty}' cannot use 'HasJsonProp

Error message

Complex property '{complexProperty}' cannot use 'HasJsonPropertyName()' because it is not contained within a JSON-mapped type. Use 'ToJson()' to map the complex property to a JSON column, or ensure it is contained within a type that is mapped to JSON.

What it means

ValidatePropertyMapping (RelationalModelValidator.cs:293-298) throws when a complex property has a HasJsonPropertyName configured but its declaring type is NOT mapped to JSON. HasJsonPropertyName only makes sense for a property nested inside a JSON document; without a containing JSON column there is nowhere for the JSON key to live, so the configuration is rejected.

Source

Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:295

        {
            throw new InvalidOperationException(
                RelationalStrings.ComplexPropertyOptionalTableSharing(complexProperty.ComplexType.DisplayName(), complexProperty.Name));
        }

        if (complexProperty.GetJsonPropertyName() != null)
        {
            if (complexProperty.ComplexType.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)?.Value is string columnName)
            {
                throw new InvalidOperationException(
                    RelationalStrings.ComplexPropertyBothJsonColumnAndJsonPropertyName(
                        $"{complexProperty.DeclaringType.DisplayName()}.{complexProperty.Name}",
                        columnName,
                        complexProperty.GetJsonPropertyName()));
            }

            if (!complexProperty.DeclaringType.IsMappedToJson())
            {
                throw new InvalidOperationException(
                    RelationalStrings.ComplexPropertyJsonPropertyNameWithoutJsonMapping(
                        $"{complexProperty.DeclaringType.DisplayName()}.{complexProperty.Name}"));
            }
        }

        if (complexProperty.ComplexType.IsMappedToJson())
        {
            if (!complexProperty.DeclaringType.IsMappedToJson()
                && complexProperty.DeclaringType is IComplexType)
            {
                // Issue #36558
                throw new InvalidOperationException(
                    RelationalStrings.NestedComplexPropertyJsonWithTableSharing(
                        $"{complexProperty.DeclaringType.DisplayName()}.{complexProperty.Name}",
                        complexProperty.DeclaringType.DisplayName()));
            }

            ValidateJsonProperties(complexProperty.ComplexType);

View on GitHub (pinned to dbf9771522)

Solutions

  1. Map the containing type to JSON: add ToJson() on the root complex property/entity so the HasJsonPropertyName has a home.
  2. If the property should remain table-mapped (flattened to columns), remove HasJsonPropertyName() and use HasColumnName() instead.
  3. Ensure the entire chain from the entity root down is JSON-mapped before setting JSON property names on nested members.

Example fix

// before - HasJsonPropertyName with no JSON container
modelBuilder.Entity<Customer>().OwnsOne(c => c.Profile, p =>
{
    p.HasJsonPropertyName("profile"); // Customer not mapped to JSON -> throws
});

// after - either map container to JSON
modelBuilder.Entity<Customer>().OwnsOne(c => c.Details, d =>
{
    d.ToJson("DetailsJson");
    d.OwnsOne(c => c.Profile, p => p.HasJsonPropertyName("profile"));
});
// OR drop HasJsonPropertyName and use column mapping for table-shared complex type
Defensive patterns

Strategy: validation

Validate before calling

foreach (var et in context.Model.GetEntityTypes())
{
    foreach (var cp in et.GetComplexProperties())
    {
        if (cp.GetJsonPropertyName() != null && !cp.DeclaringType.IsMappedToJson())
        {
            // will throw - map the containing type to JSON or use HasColumnName instead.
        }
    }
}

Prevention

When it happens

Trigger: Calling HasJsonPropertyName("key") on a complex property whose owning entity/complex type is mapped to a regular table (not via ToJson). Thrown at model validation.

Common situations: Configuring JSON property names on complex types that are table-mapped; applying a HasJsonPropertyName convention to non-JSON entities; partial JSON migration where the root ToJson() was forgotten.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/afd65db968c25d12. Report an issue: GitHub.