dotnet/efcore · error · InvalidOperationException

Entity type ' ' has a split mapping and is an optional…

Error message

Entity type '{entityType}' has a split mapping and is an optional dependent sharing a store object, but it doesn't map any required non-shared property to the main store object. Keep at least one required non-shared property mapped to a column on '{storeObject}' or mark '{entityType}' as a required dependent by calling '{requiredDependentConfig}'.

What it means

When the entity is an optional dependent that shares a store object (table splitting), an all-NULL row is indistinguishable from 'no dependent'. To disambiguate, the validator requires at least one REQUIRED (non-nullable), non-shared property mapped to the main store object. If none is found and a row-internal FK is not IsRequiredDependent, it throws and tells you how to mark the dependent required.

Solutions

  1. Add a required (non-nullable), non-shared property mapped to the main store object so a dependent row is always detectable.
  2. Mark the dependent as required exactly as the message suggests: modelBuilder.Entity<Principal>().OwnsOne(p => p.Dependent).Navigation(p => p.Dependent).IsRequired() (or HasPrincipalKey/required navigation) so IsRequiredDependent becomes true.
  3. Make an existing nullable column non-nullable if business rules allow.

Example fix

// before — optional dependent, all columns nullable -> [443]
modelBuilder.Entity<Order>()
    .OwnsOne(o => o.BillingInfo, b =>
    {
        b.Property(bi => bi.TaxId).IsRequired(false);
        b.ToTable("Order");
    });

// after — mark the owned navigation required
modelBuilder.Entity<Order>()
    .OwnsOne(o => o.BillingInfo, b =>
    {
        b.Property(bi => bi.TaxId).IsRequired();
        b.ToTable("Order");
    })
    .Navigation(o => o.BillingInfo)
    .IsRequired();
Defensive patterns

Strategy: validation

Validate before calling

// For an optional dependent sharing a table, ensure a required non-shared property OR mark required.
static bool OptionalDependentIsDistinguishable(IEntityType et, StoreObjectIdentifier main)
{
    if (et.FindRowInternalForeignKeys(main).All(fk => fk.IsRequiredDependent))
        return true;
    return et.GetProperties().Any(p =>
        !p.IsPrimaryKey()
        && !p.IsNullable
        && p.GetColumnName(main) is not null
        && p.FindSharedStoreObjectRootProperty(main) is null);
}

Prevention

When it happens

Trigger: An owned/dependent entity configured as optional (nullable navigation) that also uses split mapping, where every property on the main store object is either nullable or shared from the principal (FindSharedStoreObjectRootProperty != null), so nonSharedRequiredPropertyFound stays false.

Common situations: Nullable owned navigations in table splitting with only nullable columns; refactor that made all columns nullable; TPT/TPH sharing where dependent columns overlap the principal.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/a806de288a1a16b9. Report an issue: GitHub.

Appendix: source

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

                        && !property.IsNullable
                        && property.FindSharedStoreObjectRootProperty(mainObject) == null)
                    {
                        nonSharedRequiredPropertyFound = true;
                    }
                }
            }

            if (!propertyFound)
            {
                throw new InvalidOperationException(
                    RelationalStrings.EntitySplittingMissingPropertiesMainFragment(
                        entityType.DisplayName(), mainObject.DisplayName()));
            }

            if (!nonSharedRequiredPropertyFound)
            {
                var rowInternalFk = entityType.FindRowInternalForeignKeys(mainObject).First(fk => !fk.IsRequiredDependent);
                throw new InvalidOperationException(
                    RelationalStrings.EntitySplittingMissingRequiredPropertiesOptionalDependent(
                        entityType.DisplayName(), mainObject.DisplayName(),
                        $".Navigation(p => p.{rowInternalFk.PrincipalToDependent!.Name}).IsRequired()"));
            }

            return mainObject;
        }
    }

    /// <summary>
    ///     Validates a table-specific property override for a property.
    /// </summary>
    /// <param name="property">The property to validate.</param>
    /// <param name="propertyOverride">The property override to validate.</param>
    /// <param name="logger">The logger to use.</param>
    protected virtual void ValidatePropertyOverride(
        IProperty property,
        IReadOnlyRelationalPropertyOverrides propertyOverride,

View on GitHub (pinned to 3a2006ef56)