dotnet/efcore · error · InvalidOperationException
Entity type '{entityType}' doesn't contain a property mapped
Error message
Entity type '{entityType}' doesn't contain a property mapped to the store-generated concurrency token column '{missingColumn}' which is used by another entity type sharing the table '{table}'. Add a store-generated property to '{entityType}' which is mapped to the same column; it may be in shadow state. What it means
When entity types share a table, a store-generated concurrency token column (e.g. rowversion) must be mapped by every sharing type, because SQL Server generates a new token value on any update to the row regardless of which entity triggered it. If one entity omits the token property, an update through that entity would not receive the new token and the next optimistic-concurrency check on the other entity would fail. The validator at line 1442-1458 builds the set of missing token columns per entity and throws MissingConcurrencyColumn.
Source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:1455
if (missingConcurrencyTokens != null)
{
missingConcurrencyTokens.Clear();
foreach (var (concurrencyColumn, concurrencyProperties) in concurrencyColumns!)
{
if (TableSharingConcurrencyTokenConvention.IsConcurrencyTokenMissing(concurrencyProperties, entityType, mappedTypes))
{
missingConcurrencyTokens.Add(concurrencyColumn);
}
}
}
ValidateCompatible(entityType, storeObject, propertyMappings, missingConcurrencyTokens, logger);
if (missingConcurrencyTokens != null)
{
foreach (var concurrencyColumn in missingConcurrencyTokens)
{
throw new InvalidOperationException(
RelationalStrings.MissingConcurrencyColumn(
entityType.DisplayName(), concurrencyColumn, storeObject.DisplayName()));
}
}
}
var columnOrders = new Dictionary<int, List<string>>();
foreach (var property in propertyMappings.Values)
{
var columnOrder = property.GetColumnOrder(storeObject);
if (!columnOrder.HasValue)
{
continue;
}
var columns = columnOrders.GetOrAddNew(columnOrder.Value);
columns.Add(property.GetColumnName(storeObject)!);
}View on GitHub (pinned to dbf9771522)
Solutions
- Add a shadow or declared concurrency-token property to the dependent entity mapped to the same column: .Property<byte[]>("RowVersion").IsRowVersion().HasColumnName("RowVersion").
- Remove the concurrency token from the principal if optimistic concurrency is not needed for the shared table.
- Stop sharing the table (map the dependent to its own table) so the token scoping is per-table.
Example fix
// before
modelBuilder.Entity<Order>()
.Property(o => o.RowVersion).IsRowVersion();
modelBuilder.Entity<Order>().OwnsOne(o => o.Details); // no token on Details
// after - add a shadow token property on the dependent mapped to the same column
modelBuilder.Entity<Order>()
.OwnsOne(o => o.Details, d =>
{
d.Property<byte[]>("RowVersion").IsRowVersion().HasColumnName("RowVersion");
}); Defensive patterns
Strategy: validation
Validate before calling
foreach (var et in modelBuilder.Model.GetEntityTypes())
{
var table = et.GetTableName();
if (table == null) continue;
var storeObj = StoreObjectIdentifier.Table(table, et.GetSchema());
// gather token columns used by any type sharing this table
var tokenCols = modelBuilder.Model.GetEntityTypes()
.Where(e => e.GetTableName() == table)
.SelectMany(e => e.GetProperties())
.Where(p => p.IsConcurrencyToken && (p.ValueGenerated & ValueGenerated.OnAddOrUpdate) != 0)
.Select(p => p.GetColumnName(storeObj))
.Where(n => n != null).Distinct().ToList();
var ownCols = et.GetProperties().Select(p => p.GetColumnName(storeObj)).ToHashSet();
var missing = tokenCols.Where(c => !ownCols.Contains(c)).ToList();
if (missing.Count > 0)
throw new InvalidOperationException(
$"{et.Name} sharing table '{table}' is missing concurrency columns: {string.Join(", ", missing)}");
} Prevention
- When you add a rowversion/Timestamp to a principal that uses table splitting, mirror it as a shadow IsRowVersion() property on every dependent sharing the table.
- Run the model through FinalizeModel() in tests to surface this at build time.
- Centralize rowversion configuration in an extension applied to all sharing types.
When it happens
Trigger: An owned/table-splitting entity whose principal has a `[Timestamp]`/`.IsRowVersion()`/`.IsConcurrencyToken()` property, but the dependent entity does not declare a property mapped to the same concurrency column. Detected by TableSharingConcurrencyTokenConvention.IsConcurrencyTokenMissing.
Common situations: Adding a rowversion column to the principal after the dependent was already configured; the dependent was generated by scaffolding before the token existed; using table splitting and forgetting to mirror the token.
Related errors
- The entity type '{entityType}' has property '{property}' con
- The type of the etag property '{property}' on '{entityType}'
- The entity type '{entityType}' is mapped to the stored proce
- Entity type '{entityType}' is an optional dependent using ta
- 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/87568747cece950e.
Report an issue: GitHub.