dotnet/efcore · error · InvalidOperationException

The entity type ' ' cannot be instantiated because its…

Error message

The entity type '{entityType}' cannot be instantiated because its corresponding CLR type is abstract, but the entity type was mapped to '{storeObject}' using the 'TPC' mapping strategy. Only instantiable types should be mapped. See https://go.microsoft.com/fwlink/?linkid=2130430 for more information.

What it means

An entity type whose CLR type is abstract (cannot be instantiated) was mapped to a concrete store object (table/view/function/SQL query) under the TPC (Table-Per-Concrete-Type) mapping strategy. TPC generates one table per concrete type and inserts/queries instances of it, so an abstract type with a store object would be unreachable at runtime yet create a phantom table. EF rejects this at model validation.

Solutions

  1. Remove the ToTable/ToView/ToFunction/ToSqlQuery mapping from the abstract base type so it has no store object.
  2. Make the CLR type concrete (non-abstract) if it genuinely needs its own table under TPC.
  3. Move the store mapping to each concrete derived type instead of the abstract base.

Example fix

// before
public abstract class Animal { public int Id { get; set; } }
modelBuilder.Entity<Animal>().ToTable("Animals").UseTpcMappingStrategy();

// after - abstract root has no table under TPC
modelBuilder.Entity<Animal>().UseTpcMappingStrategy();
modelBuilder.Entity<Dog>().ToTable("Dogs");
modelBuilder.Entity<Cat>().ToTable("Cats");
Defensive patterns

Strategy: validation

Validate before calling

// For each TPC entity, ensure abstract types have no store object.
foreach (var et in db.Model.GetEntityTypes())
{
    var strategy = et[RelationalAnnotationNames.MappingStrategy] as string;
    if (strategy != RelationalAnnotationNames.TpcMappingStrategy) continue;
    if (et.ClrType.IsAbstract)
    {
        var store = et.GetSchemaQualifiedTableName() ?? et.GetSchemaQualifiedViewName() ?? et.GetFunctionName() ?? et.GetSqlQuery();
        if (store != null) Console.WriteLine($"Abstract TPC type {et.Name} maps to {store}");
    }
}

Type guard

static bool IsInstantiable(Type t) => !t.IsAbstract && !t.IsGenericTypeDefinition && !(t.IsGenericType && t.GetGenericArguments().Any(a => a.IsGenericParameter)) && t.GetConstructor(Type.EmptyTypes) != null || !t.IsAbstract;

Try / catch

try { _ = db.Model; } catch (InvalidOperationException ex) when (ex.Message.Contains("cannot be instantiated because its corresponding CLR type is abstract") && ex.Message.Contains("TPC")) { // remove the store mapping from the abstract type or make it concrete }

Prevention

When it happens

Trigger: ValidateInheritanceMapping / ValidateMappingStrategy checks mappingStrategy == TpcMappingStrategy, !entityType.ClrType.IsInstantiable(), and a non-null store object (table/view/function/query/sproc); throws RelationalStrings.AbstractTpc. Triggered when an abstract base class in a TPC hierarchy has ToTable/ToView/etc.

Common situations: Marking the base class 'abstract' but leaving ToTable("Bases") on it after switching to TPC; introducing an abstract intermediate type and not removing its mapping; TPC migration from TPH where the root entity had a table.

Related errors


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

Appendix: source

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

        IEntityType entityType,
        IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
    {
        var mappingStrategy = (string?)entityType[RelationalAnnotationNames.MappingStrategy];
        if (mappingStrategy != null)
        {
            ValidateMappingStrategy(entityType, mappingStrategy);
            var storeObjectName = entityType.GetSchemaQualifiedTableName()
                ?? entityType.GetSchemaQualifiedViewName()
                ?? entityType.GetFunctionName()
                ?? entityType.GetSqlQuery()
                ?? entityType.GetInsertStoredProcedure()?.GetSchemaQualifiedName()
                ?? entityType.GetDeleteStoredProcedure()?.GetSchemaQualifiedName()
                ?? entityType.GetUpdateStoredProcedure()?.GetSchemaQualifiedName();
            if (mappingStrategy == RelationalAnnotationNames.TpcMappingStrategy
                && !entityType.ClrType.IsInstantiable()
                && storeObjectName != null)
            {
                throw new InvalidOperationException(
                    RelationalStrings.AbstractTpc(entityType.DisplayName(), storeObjectName));
            }
        }

        if (entityType.BaseType != null)
        {
            if (mappingStrategy != null
                && mappingStrategy != (string?)entityType.BaseType[RelationalAnnotationNames.MappingStrategy])
            {
                throw new InvalidOperationException(
                    RelationalStrings.DerivedStrategy(entityType.DisplayName(), mappingStrategy));
            }

            return;
        }

        // Hierarchy mapping strategy must be the same across all types of mappings (only for root types)
        if (entityType.FindDiscriminatorProperty() != null)

View on GitHub (pinned to 3a2006ef56)