dotnet/efcore · error · InvalidOperationException
Entity type '{entityType}' is an optional dependent using ta
Error message
Entity type '{entityType}' is an optional dependent using table sharing and containing other dependents without any required non shared property to identify whether the entity exists. If all nullable properties contain a 'null' value in database then an object instance won't be created in the query causing nested dependent's values to be lost. Add a required property to create instances with 'null' values for other properties or mark the incoming navigation as required to always create an instance. What it means
When an optional dependent shares a table with its principal (table splitting / owned types) and has no required non-shared column, EF cannot distinguish a row of all-NULL dependent columns from an absent dependent — it will materialize null and silently lose any nested dependents' data. The validator at line 1098-1104 escalates this from a warning to an error when the optional dependent itself has nested dependents mapped to the same table, because data loss is then guaranteed on round-trip.
Source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:1102
continue;
}
var columnName = property.GetColumnName(tableIdentifier);
if (columnName != null)
{
if (!principalColumns.Contains(columnName))
{
requiredNonSharedColumnFound = true;
break;
}
}
}
if (!requiredNonSharedColumnFound)
{
if (entityType.GetReferencingForeignKeys().Select(e => e.DeclaringEntityType).Any(t => mappedTypes.Contains(t)))
{
throw new InvalidOperationException(
RelationalStrings.OptionalDependentWithDependentWithoutIdentifyingProperty(entityType.DisplayName()));
}
logger.OptionalDependentWithoutIdentifyingPropertyWarning(entityType);
}
}
(List<IEntityType> EntityTypes, bool Optional) GetPrincipalEntityTypes(IEntityType entityType)
{
if (!principalEntityTypesMap.TryGetValue(entityType, out var tuple))
{
var list = new List<IEntityType>();
var optional = false;
foreach (var foreignKey in entityType.FindForeignKeys(entityType.FindPrimaryKey()!.Properties))
{
var principalEntityType = foreignKey.PrincipalEntityType;
if (foreignKey.PrincipalEntityType.IsAssignableFrom(foreignKey.DeclaringEntityType)
|| !mappedTypes.Contains(principalEntityType))View on GitHub (pinned to dbf9771522)
Solutions
- Add a required (non-nullable) property to the optional dependent that is not shared with the principal — e.g. a boolean 'Exists' flag or a required value — so EF can detect instance presence.
- Make the ownership required (.OwnsOne(...).IsRequired() or required navigation) so an instance is always created.
- Map the optional dependent to its own table instead of sharing, removing the ambiguity.
Example fix
// before
modelBuilder.Entity<Order>(b =>
{
b.OwnsOne(o => o.Details, d =>
{
d.OwnsOne(x => x.Nested); // optional, no required non-shared column
});
});
// after - add a required sentinel property
modelBuilder.Entity<Order>(b =>
{
b.OwnsOne(o => o.Details, d =>
{
d.Property<bool>("Exists").IsRequired();
d.OwnsOne(x => x.Nested);
});
}); Defensive patterns
Strategy: validation
Validate before calling
// For each optional dependent sharing a table that itself has dependents,
// ensure a required non-shared column exists.
foreach (var et in modelBuilder.Model.GetEntityTypes())
{
var ownership = et.FindOwnership();
if (ownership == null || ownership.IsRequired) continue;
var principal = ownership.PrincipalEntityType;
var table = et.GetTableName();
if (table == null) continue;
bool hasNestedDependent = et.GetReferencingForeignKeys()
.Any(rfk => rfk.DeclaringEntityType.GetTableName() == table);
if (!hasNestedDependent) continue;
var principalCols = principal.GetProperties()
.Select(p => p.GetColumnName(StoreObjectIdentifier.Table(table, et.GetSchema())))
.Where(n => n != null).ToHashSet();
bool hasRequiredNonShared = et.GetProperties().Any(p =>
!p.IsPrimaryKey() && !p.IsNullable
&& p.GetColumnName(StoreObjectIdentifier.Table(table, et.GetSchema())) is string col
&& !principalCols.Contains(col));
if (!hasRequiredNonShared)
throw new InvalidOperationException(
$"Optional dependent {et.Name} needs a required non-shared property or a required navigation.");
} Prevention
- Add a required non-nullable sentinel property (e.g. bool Exists) to optional owned types that contain nested owned types.
- Prefer .IsRequired() on ownerships when the dependent always exists.
- Avoid nesting owned types under an optional owned type; split to a separate table instead.
When it happens
Trigger: An owned entity configured as optional (`IsRequired(false)` on the ownership or nullable navigation) whose columns are all nullable/shared-with-principal, AND that owned entity contains further owned entities. The check at line 1100 confirms nested dependents exist before throwing.
Common situations: Owned types with optional ownership and no required discriminator/flag property; making a previously-required navigation optional; adding nested owned types to an optional owned entity.
Related errors
- The table '{table}' cannot be used for entity type '{entityT
- The table '{table}' cannot be used for entity type '{entityT
- The table '{table}' cannot be used for entity type '{entityT
- The table '{table}' cannot be used for entity type '{entityT
- The table '{table}' cannot be used for entity type '{entityT
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/b9f36265e8d4ba2e.
Report an issue: GitHub.