dotnet/efcore · error · InvalidOperationException

Entity type '{entityType}' has a split mapping and is an opt

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

Thrown during model validation when an entity type uses entity splitting (maps to multiple table fragments) and is also an optional dependent (its row-internal foreign key is not required). EF requires that at least one non-shared, non-nullable property be mapped to the main store object, so a row can be distinguished from 'all nulls'. Without it EF cannot determine whether the optional dependent row exists. The validator at RelationalModelValidator.cs:2466-2507 iterates all non-PK properties and checks IsNullable and FindSharedStoreObjectRootProperty against the main table.

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 dbf9771522)

Solutions

  1. Make at least one non-shared property non-nullable and map it to the main table (configure it as required).
  2. Call .Navigation(p => p.YourNavigation).IsRequired() on the owning entity to mark the dependent as required, so EF doesn't need the sentinel column.
  3. Move the split fragment mapping so that at least one required property stays on the main table rather than on the split fragment.
  4. Add a non-nullable discriminator or sentinel property to the main table.

Example fix

// before: all properties on main table are nullable or shared
modelBuilder.Entity<Order>()
    .OwnsOne(o => o.Details, db =>
    {
        db.ToTable("OrderDetailsSplit");
        db.Property(d => d.Notes).HasColumnName("DetailsNotes"); // only nullable props remain on main
    });

// after: mark the owned navigation as required
modelBuilder.Entity<Order>()
    .OwnsOne(o => o.Details, db =>
    {
        db.ToTable("OrderDetailsSplit");
    })
    .Navigation(o => o.Details)
    .IsRequired();
Defensive patterns

Strategy: validation

Validate before calling

// Before finalizing the model, check that each split-mapped optional dependent has a required non-shared property on the main table.
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
    var fragments = entityType.GetMappingFragments(StoreObjectType.Table).ToList();
    if (fragments.Count == 0) continue;
    var mainObj = StoreObjectIdentifier.Create(entityType, StoreObjectType.Table).GetValueOrDefault();
    var hasNonRequiredFk = entityType.FindRowInternalForeignKeys(mainObj).Any(fk => !fk.IsRequiredDependent);
    if (!hasNonRequiredFk) continue;
    var hasRequiredNonShared = entityType.GetProperties()
        .Where(p => !p.IsPrimaryKey())
        .Any(p => p.GetColumnName(mainObj) != null && !p.IsNullable
                  && p.FindSharedStoreObjectRootProperty(mainObj) == null);
    if (!hasRequiredNonShared)
        Console.WriteLine($"WARN: {entityType.DisplayName()} needs a required property on {mainObj.DisplayName()} or IsRequired() on its navigation.");
}

Prevention

When it happens

Trigger: Called by ValidateMainMapping when entityType.GetMappingFragments() returns table or view fragments AND entityType.FindRowInternalForeignKeys(mainObject) contains an FK where IsRequiredDependent is false, AND no non-PK property satisfies (GetColumnName(mainObject) != null && !IsNullable && FindSharedStoreObjectRootProperty(mainObject) == null).

Common situations: Using owned entity types with entity splitting (SplitToTable) where all non-key properties on the main table are either nullable, shared (FK columns from the principal), or moved to a split fragment. Common when migrating from TPH/owned types to splitting and accidentally making all columns nullable.

Related errors


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