dotnet/efcore · error · InvalidOperationException
The check constraints
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
Two check constraints across entity types are mapped to the same database check name (typically because both entity types map to the same table, e.g. TPH or split-table scenarios), but their defining SQL differs. CheckConstraint.AreCompatible throws when the SQL strings are unequal and shouldThrow is set during model validation.
Solutions
- Open both check constraints named in the message and compare their SQL character-by-character.
- Make the SQL strings identical (normalize whitespace, casing, identifiers) so the constraint is unambiguous.
- If the constraints are genuinely different, give them distinct constraint names so each maps independently.
Example fix
// before
modelBuilder.Entity<Order>().HasCheckConstraint("ck_amount", "Amount > 0");
modelBuilder.Entity<OrderLine>() // mapped to same table via splitting
.HasCheckConstraint("ck_amount", "[Amount] > 0");
// after (normalize SQL text)
modelBuilder.Entity<Order>().HasCheckConstraint("ck_amount", "[Amount] > 0");
modelBuilder.Entity<OrderLine>()
.HasCheckConstraint("ck_amount", "[Amount] > 0"); Defensive patterns
Strategy: validation
Validate before calling
// Before finalizing, verify every shared check-constraint name has identical SQL across entity types mapped to the same table
foreach (var et in modelBuilder.Model.GetEntityTypes())
{
foreach (var ck in et.GetDeclaredCheckConstraints())
{
foreach (var other in modelBuilder.Model.GetEntityTypes().Where(e => e != et))
{
var dup = other.FindCheckConstraint(ck.ModelName);
if (dup is not null
&& et.GetSchemaQualifiedTableName() == other.GetSchemaQualifiedTableName()
&& ck.Sql != dup.Sql)
{
throw new InvalidOperationException($"Check '{ck.ModelName}' SQL differs between {et.Name} and {other.Name}.");
}
}
}
} Prevention
- When two entity types share a table, centralize their check-constraint SQL in one location to guarantee identical strings.
- Normalize SQL text (whitespace, quoting) so the string comparison matches intent.
- Add a unit test that asserts no two same-named checks on the same table diverge in SQL.
When it happens
Trigger: Two entity types mapped to the same table each declare a check constraint resolving to the same constraint name but with different SQL text; common in TPT/TPH or entity splitting. Fires during RelationalModelValidator / convention passes with shouldThrow=true.
Common situations: Whitespace/casing differences that look identical to a human but compare unequal; intentional reuse of a name across two entities with divergent intent; legacy schemas where two developers added different constraints to the same logical table.
Related errors
- Both entity type ' ' and ' ' were configured to use ' '…
- Current value parameter
- Entity type ' ' doesn't contain a property mapped to the…
- Entity type ' ' has a split mapping, but it doesn't map any…
- Entity type ' ' has a split mapping for ' ', but it doesn't…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/75ec2b523b237b3c.
Report an issue: GitHub.
Appendix: 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 3a2006ef56)