dotnet/efcore · error · InvalidOperationException
The entity type '{entityType}' cannot be instantiated becaus
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/stored procedure) using the TPC (Table-per-Concrete-type) mapping strategy. TPC gives each type its own table, so abstract types - which can never be queried or inserted as instances - must not be mapped to a store object. Thrown from ValidateInheritanceMapping when mappingStrategy == Tpc and the CLR type is not instantiable.
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 dbf9771522)
Solutions
- Remove the table/view/function mapping from the abstract base (do not call ToTable on it; in TPC the base should not be a store object).
- If the base must participate, make its CLR class concrete (sealed or non-abstract), though usually the right fix is to leave it abstract and unmapped.
- Confirm UseTpcMappingStrategy is only declared on the concrete root or each concrete derived type as supported by the provider.
Example fix
// before
public abstract class Animal { public int Id { get; set; } }
modelBuilder.Entity<Animal>().ToTable("Animals").UseTpcMappingStrategy();
// after (abstract base is not mapped to a store object in TPC)
public abstract class Animal { public int Id { get; set; } }
modelBuilder.Entity<Animal>().UseTpcMappingStrategy();
// 'Animals' table is removed; only Cat/Dog tables exist Defensive patterns
Strategy: validation
Validate before calling
using (var ctx = new MyContext()) { ctx.Model.ToDebugString(); }
// Assert that no abstract entity in a TPC hierarchy is mapped to a store object.
foreach (var et in ctx.Model.GetEntityTypes())
{
var strategy = et.GetMappingStrategy();
if (strategy == "TPC" && et.ClrType.IsAbstract)
{
var table = et.GetSchemaQualifiedTableName() ?? et.GetSchemaQualifiedViewName();
Debug.Assert(table == null, $"Abstract type {et.Name} is mapped to '{table}' under TPC.");
}
} Type guard
static bool IsInstantiable(Type t) => !t.IsAbstract && !t.IsGenericTypeDefinition && !t.IsInterface; // Guard: do not call .Entity<T>().ToTable(...) when IsInstantiable(typeof(T)) is false in TPC.
Try / catch
try { using var ctx = new MyContext(); _ = ctx.Model; }
catch (InvalidOperationException ex) when (ex.Message.Contains("abstract") && ex.Message.Contains("TPC"))
{ log.Error("Abstract type mapped in TPC; remove its ToTable mapping: {Msg}", ex.Message); throw; } Prevention
- Never call ToTable/ToView on an abstract base in a TPC hierarchy.
- Add a startup model-build test that asserts abstract TPC types have no store object.
- When refactoring a base class to abstract, sweep OnModelCreating for stray mappings.
When it happens
Trigger: Calling modelBuilder.Entity<AbstractBase>().UseTpcMappingStrategy() where AbstractBase is an abstract C# class that also resolves to a table (e.g. via ToTable or default convention); configuring a TPC hierarchy root whose base class is abstract and ends up with a table mapping.
Common situations: Migrating from TPH/TPC and forgetting to mark the abstract base as unmapped; convention-based ToTable on an abstract root in a TPC hierarchy; refactoring a base class to abstract without removing its table mapping.
Related errors
- The derived entity type '{entityType}' was configured with t
- The mapping strategy '{mappingStrategy}' specified on '{enti
- The specified discriminator value '{value}' for '{entityType
- The short name for '{entityType1}' is '{discriminatorValue}'
- Both '{entityType}' and '{otherEntityType}' are mapped to th
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/3308cfbd66ddfb4d.
Report an issue: GitHub.