dotnet/efcore · error · InvalidOperationException
The check constraints '{checkConstraint1}' on '{entityType1}
Error message
The check constraints '{checkConstraint1}' on '{entityType1}' and '{checkConstraint2}' on '{entityType2}' are both mapped to '{checkConstraintName}', but with different defining SQL. What it means
Thrown by CheckConstraint.AreCompatible during model validation/finalization when two check constraints are mapped to the same name (on table-sharing entity types) but have different defining SQL. EF cannot emit a single database constraint satisfying both, so it rejects the conflict rather than silently picking one.
Source
Thrown at src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs:198
((InternalCheckConstraintBuilder)existingCheckConstraint.Builder).MergeAnnotationsFrom(
(CheckConstraint)detachedCheckConstraint);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static bool AreCompatible(
IReadOnlyCheckConstraint checkConstraint,
IReadOnlyCheckConstraint duplicateCheckConstraint,
in StoreObjectIdentifier storeObject,
bool shouldThrow)
=> checkConstraint.Sql == duplicateCheckConstraint.Sql
|| (shouldThrow
? throw new InvalidOperationException(
RelationalStrings.DuplicateCheckConstraintSqlMismatch(
checkConstraint.ModelName,
checkConstraint.EntityType.DisplayName(),
duplicateCheckConstraint.ModelName,
duplicateCheckConstraint.EntityType.DisplayName(),
checkConstraint.GetName(storeObject)))
: false);
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual InternalCheckConstraintBuilder Builder
{
[DebuggerStepThrough]
get => _builder ?? throw new InvalidOperationException(CoreStrings.ObjectRemovedFromModel(ModelName));View on GitHub (pinned to dbf9771522)
Solutions
- Make the SQL bodies identical (exact string match, including whitespace) for constraints that should share a name.
- Give each conflicting constraint a distinct explicit Name so they do not collapse onto one database constraint.
- Remove the redundant constraint from one of the sharing types if the semantics are equivalent.
- If genuinely different predicates are needed, accept that they cannot share a name and explicitly disambiguate.
Example fix
// before (TPH: base + derived share table, same name, different SQL)
modelBuilder.Entity<Person>().HasCheckConstraint("CK_Age", "Age > 0");
modelBuilder.Entity<Employee>().HasCheckConstraint("CK_Age", "Age >= 18");
// after (make SQL agree, or rename one)
modelBuilder.Entity<Person>().HasCheckConstraint("CK_Age", "Age >= 18");
// derived no longer redeclares; or:
modelBuilder.Entity<Employee>().HasCheckConstraint("CK_Employee_Age", "Age >= 18"); Defensive patterns
Strategy: validation
Validate before calling
// For entity types sharing a table, verify same-named constraints share SQL
foreach (var cc in entityType.GetCheckConstraints())
{
foreach (var other in sharingTypes.SelectMany(t => t.GetCheckConstraints()))
{
if (cc.GetName(table) == other.GetName(table) && cc.Sql != other.Sql)
throw new InvalidOperationException($"SQL mismatch for {cc.GetName(table)}");
}
} Prevention
- For table-sharing entity types, keep a single source of truth for shared constraint SQL.
- Compare SQL bodies with normalization (trim, lower) when reusing names across shared tables.
- Prefer distinct constraint names per type unless the SQL is intentionally identical.
When it happens
Trigger: Entity types that share a table (TPH hierarchies, table splitting, owned types mapped to the same table) each declare a HasCheckConstraint with the same resolved name but different SqlBody strings. Surfaced when the relational model is built and compatibility is checked with shouldThrow=true.
Common situations: TPH hierarchy where base and derived both add CK_X with subtly different SQL (whitespace, casing, or genuinely different predicates). Owned entity sharing the owner's table adds a constraint that collides by default-name with the owner's.
Related errors
- The check constraint '{checkConstraint}' cannot be added to
- The foreign keys {foreignKeyProperties1} on '{entityType1}'
- The foreign keys {foreignKeyProperties1} on '{entityType1}'
- The foreign keys {foreignKeyProperties1} on '{entityType1}'
- The foreign keys {foreignKeyProperties1} on '{entityType1}'
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/75ec2b523b237b3c.
Report an issue: GitHub.