{"record":{"id":"75ec2b523b237b3c","repo":"dotnet/efcore","slug":"the-check-constraints-checkconstraint1-on-en","errorCode":null,"errorMessage":"The check constraints '{checkConstraint1}' on '{entityType1}' and '{checkConstraint2}' on '{entityType2}' are both mapped to '{checkConstraintName}', but with different defining SQL.","messagePattern":"The check constraints '(.+?)' on '(.+?)' and '(.+?)' on '(.+?)' are both mapped to '(.+?)', but with different defining SQL\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs","lineNumber":198,"sourceCode":"\n        ((InternalCheckConstraintBuilder)existingCheckConstraint.Builder).MergeAnnotationsFrom(\n            (CheckConstraint)detachedCheckConstraint);\n    }\n\n    /// <summary>\n    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to\n    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in\n    ///     any release. You should only use it directly in your code with extreme caution and knowing that\n    ///     doing so can result in application failures when updating to a new Entity Framework Core release.\n    /// </summary>\n    public static bool AreCompatible(\n        IReadOnlyCheckConstraint checkConstraint,\n        IReadOnlyCheckConstraint duplicateCheckConstraint,\n        in StoreObjectIdentifier storeObject,\n        bool shouldThrow)\n        => checkConstraint.Sql == duplicateCheckConstraint.Sql\n            || (shouldThrow\n                ? throw new InvalidOperationException(\n                    RelationalStrings.DuplicateCheckConstraintSqlMismatch(\n                        checkConstraint.ModelName,\n                        checkConstraint.EntityType.DisplayName(),\n                        duplicateCheckConstraint.ModelName,\n                        duplicateCheckConstraint.EntityType.DisplayName(),\n                        checkConstraint.GetName(storeObject)))\n                : false);\n\n    /// <summary>\n    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to\n    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in\n    ///     any release. You should only use it directly in your code with extreme caution and knowing that\n    ///     doing so can result in application failures when updating to a new Entity Framework Core release.\n    /// </summary>\n    public virtual InternalCheckConstraintBuilder Builder\n    {\n        [DebuggerStepThrough]\n        get => _builder ?? throw new InvalidOperationException(CoreStrings.ObjectRemovedFromModel(ModelName));","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs#L180-L216","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nmodelBuilder.Entity<Order>().HasCheckConstraint(\"ck_amount\", \"Amount > 0\");\nmodelBuilder.Entity<OrderLine>()  // mapped to same table via splitting\n    .HasCheckConstraint(\"ck_amount\", \"[Amount] > 0\");\n\n// after (normalize SQL text)\nmodelBuilder.Entity<Order>().HasCheckConstraint(\"ck_amount\", \"[Amount] > 0\");\nmodelBuilder.Entity<OrderLine>()\n    .HasCheckConstraint(\"ck_amount\", \"[Amount] > 0\");","handlingStrategy":"validation","validationCode":"// Before finalizing, verify every shared check-constraint name has identical SQL across entity types mapped to the same table\nforeach (var et in modelBuilder.Model.GetEntityTypes())\n{\n    foreach (var ck in et.GetDeclaredCheckConstraints())\n    {\n        foreach (var other in modelBuilder.Model.GetEntityTypes().Where(e => e != et))\n        {\n            var dup = other.FindCheckConstraint(ck.ModelName);\n            if (dup is not null\n                && et.GetSchemaQualifiedTableName() == other.GetSchemaQualifiedTableName()\n                && ck.Sql != dup.Sql)\n            {\n                throw new InvalidOperationException($\"Check '{ck.ModelName}' SQL differs between {et.Name} and {other.Name}.\");\n            }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["relational","check-constraints","table-sharing","model-validation"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}