dotnet/efcore · error · InvalidOperationException

Entity type ' ' doesn't contain a property mapped to the…

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

Thrown during shared-table column validation when entity types share a table and one of them declares a store-generated concurrency token (e.g. rowversion/xmin), but another entity type sharing that same table has no property mapped to that concurrency-token column. EF requires every type sharing the table to observe the token so optimistic-concurrency checks stay consistent across the shared row.

Solutions

  1. Add a shadow or explicit concurrency-token property on the entity type named in the message, mapped to the same column (e.g. .Property<byte[]>("RowVersion").IsRowToken().HasColumnName(<column>)).
  2. Remove the concurrency token from the other entity if all partners do not need it.
  3. Stop table-sharing (map the dependent to its own table) so the token only lives on the principal.

Example fix

// before
modelBuilder.Entity<Order>().ToTable("Orders");
modelBuilder.Entity<OrderDetails>().ToTable("Orders");
modelBuilder.Entity<Order>().Property<byte[]>("RowVersion").IsRowToken();
// OrderDetails has no RowVersion -> error

// after - add a shadow concurrency token mapped to the same column
modelBuilder.Entity<OrderDetails>()
    .Property<byte[]>("RowVersion")
    .IsRowToken()
    .HasColumnName("RowVersion");
Defensive patterns

Strategy: validation

Validate before calling

// Detect table-split types missing the shared concurrency token column before validation.
var tokenProps = sharedTypes.SelectMany(t => t.GetProperties())
    .Where(p => p.IsConcurrencyToken()).Select(p => p.GetColumnName(storeObject)).ToHashSet();
foreach (var t in sharedTypes)
{
    var myTokenCols = t.GetProperties().Where(p => p.IsConcurrencyToken()).Select(p => p.GetColumnName(storeObject)).ToHashSet();
    foreach (var missing in tokenProps.Except(myTokenCols))
        Console.WriteLine($"{t.Name} missing concurrency column {missing}");
}

Try / catch

try { _ = db.Model; } catch (InvalidOperationException ex) when (ex.Message.Contains("doesn't contain a property mapped to the store-generated concurrency token column")) { // add the shadow token property in OnModelCreating }

Prevention

When it happens

Trigger: Table splitting (owned types or one-to-one PK sharing) where the principal has IsRowToken()/IsConcurrencyToken() but the dependent sharing the table has no matching property. TableSharingConcurrencyTokenConvention.IsConcurrencyTokenMissing returns true for that entity, the column is added to missingConcurrencyTokens, and the validator throws.

Common situations: Adding a [Timestamp]/byte[] RowVersion to a principal entity that is table-split with an owned/detail entity; switching a column to a concurrency token after the split was configured; using PostgreSQL xmin without exposing it on all split partners.

Related errors


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

Appendix: 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 3a2006ef56)