dotnet/efcore · error · InvalidOperationException

The check constraint '{checkConstraint}' cannot be added to

Error message

The check constraint '{checkConstraint}' cannot be added to the entity type '{entityType}' because another check constraint with the same name already exists on entity type '{conflictingEntityType}'.

What it means

Thrown by CheckConstraintConvention.ProcessEntityTypeBaseTypeChanged (CheckConstraintConvention.cs:113-117) when setting a base type creates a conflict: a derived entity type has a check constraint with the same name as one on the new base type, they are incompatible (AreCompatible returns false), and all three configuration sources — the base type's check constraint, the entity's base-type setting, and the derived check constraint — are all ConfigurationSource.Explicit. EF cannot silently resolve an explicit-vs-explicit conflict.

Source

Thrown at src/EFCore.Relational/Metadata/Conventions/CheckConstraintConvention.cs:113

    {
        var entityType = entityTypeBuilder.Metadata;
        if (newBaseType != null)
        {
            var configurationSource = entityType.GetBaseTypeConfigurationSource();
            var baseCheckConstraints = newBaseType.GetCheckConstraints().ToDictionary(c => c.ModelName);
            List<IConventionCheckConstraint>? checkConstraintsToBeDetached = null;
            List<IConventionCheckConstraint>? checkConstraintsToBeRemoved = null;
            foreach (var checkConstraint in entityType.GetDerivedTypesInclusive().SelectMany(et => et.GetDeclaredCheckConstraints()))
            {
                if (baseCheckConstraints.TryGetValue(checkConstraint.ModelName, out var baseCheckConstraint)
                    && baseCheckConstraint.GetConfigurationSource().Overrides(checkConstraint.GetConfigurationSource())
                    && !AreCompatible(checkConstraint, baseCheckConstraint))
                {
                    if (baseCheckConstraint.GetConfigurationSource() == ConfigurationSource.Explicit
                        && configurationSource == ConfigurationSource.Explicit
                        && checkConstraint.GetConfigurationSource() == ConfigurationSource.Explicit)
                    {
                        throw new InvalidOperationException(
                            RelationalStrings.DuplicateCheckConstraint(
                                checkConstraint.ModelName,
                                checkConstraint.EntityType.DisplayName(),
                                baseCheckConstraint.EntityType.DisplayName()));
                    }

                    checkConstraintsToBeRemoved ??= [];

                    checkConstraintsToBeRemoved.Add(checkConstraint);
                    continue;
                }

                if (baseCheckConstraint != null)
                {
                    checkConstraintsToBeDetached ??= [];

                    checkConstraintsToBeDetached.Add(checkConstraint);
                }

View on GitHub (pinned to dbf9771522)

Solutions

  1. Rename one of the conflicting check constraints so the names are unique across the hierarchy.
  2. Make the check constraint SQL identical on both types so AreCompatible returns true.
  3. Remove the check constraint from the derived type and keep it only on the base type.
  4. Lower the configuration source of one constraint below Explicit (e.g., via model configuration) so the convention can auto-resolve.

Example fix

// before: same name, incompatible SQL, both explicit
modelBuilder.Entity<Base>().ToTable(t => t.HasCheckConstraint("CK_Range", "Value > 0"));
modelBuilder.Entity<Derived>().HasBaseType<Base>()
    .ToTable(t => t.HasCheckConstraint("CK_Range", "Value > 100")); // throws

// after: unique names
modelBuilder.Entity<Base>().ToTable(t => t.HasCheckConstraint("CK_Base_Range", "Value > 0"));
modelBuilder.Entity<Derived>().HasBaseType<Base>()
    .ToTable(t => t.HasCheckConstraint("CK_Derived_Range", "Value > 100"));
Defensive patterns

Strategy: validation

Validate before calling

// Before setting base type, check for duplicate check constraint names.
var baseNames = baseEntityType.GetCheckConstraints().Select(c => c.ModelName).ToHashSet();
foreach (var cc in derivedEntityType.GetDerivedTypesInclusive().SelectMany(e => e.GetDeclaredCheckConstraints()))
{
    if (baseNames.Contains(cc.ModelName))
        Console.WriteLine($"Check constraint '{cc.ModelName}' on {cc.EntityType.DisplayName()} conflicts with base type.");
}

Prevention

When it happens

Trigger: Defining an inheritance hierarchy (setting HasBaseType) where both the base and derived entity types explicitly declare a check constraint with the same name but different SQL. E.g., base has .ToTable(t => t.HasCheckConstraint("CK_Range", "Col > 0")) and derived has HasCheckConstraint("CK_Range", "Col > 10").

Common situations: Adding inheritance to existing entity types that independently defined check constraints with the same name. Copy-pasting check constraint configuration across types that later become part of a hierarchy. Refactoring flat types into an inheritance hierarchy.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/1800a61a7bdb050b. Report an issue: GitHub.