Uniquify and validate Check Constraint name
See original GitHub issueHasCheckConstraint
has a name
parameter that it uses as the name of the constraint in the database.
Constraints are database objects and their names must be unique in the whole database (not just the table they belong to… as I found out the hard way by getting into a conflict 😄), so to mitigate this we should a use naming convention. Right now we have to do this manually and prepend our constraint names with the name of the table like this:
builder.HasCheckConstraint("AspNetUsers_UserName_SameAsEmail", ...);
builder.HasCheckConstraint("AspNetUsers_Created_NotBySelf", ...);
builder.HasCheckConstraint("AspNetUsers_LastLogonAtUtc_LastLogonInfo_BothOrNonePresent", ...);
That’s why I created an extension method that does this for me:
/// <summary>
/// Calls <see cref="RelationalEntityTypeBuilderExtensions.HasCheckConstraint{TEntity}(EntityTypeBuilder{TEntity}, string, string)"/>
/// with the name of the table prepended to <paramref name="name"/> to prevent constraint name conflicts.
/// </summary>
public static EntityTypeBuilder<TEntity> HasLocalCheckConstraint<TEntity>(this EntityTypeBuilder<TEntity> entityTypeBuilder, string name, string sql) where TEntity : class
{
var tableName = entityTypeBuilder.Metadata.GetTableName();
return entityTypeBuilder.HasCheckConstraint(FormattableString.Invariant($"CK_{tableName}_{name}"), sql);
}
and also uses the standard CK
prefix as a convention. Then I only use HasLocalCheckConstraint
to create check constraints and have to be careful to never call HasCheckConstraint
directly.
It would be nice if EF Core had this feature builtin or could do this automatically. It would also ensure that all database object created by EF Core follow the naming convention that it uses now, with a two letter prefix for the type of the object (such as PK
, AK
, FK
, IX
).
Just changing the existing behavior of HasCheckConstraint
would of course be a huge breaking change and adding a parameter to opt-in to this behavior would be clumsy and it should really be the default in my opinion.
I also noticed that while methods like HasDefaultValue
, HasDefaultValueSql
and HasComputedColumnSql
follow the convention where if they take SQL, their names end with Sql
, and if there’s an expression-based version, it doesn’t. What if the existing method was deprecated and the new name would be HasCheckConstraintSql
with this new naming behavior? If there’s an expression-based version added later (#15409), that would take the name HasCheckConstraint
and still follow the new behavior because it would be a separate overload from the deprecated one (or the deprecated one could be gone by that time).
To allow for compatibility with existing constraint names, the new method could take an optional parameter (bool or enum) to disable the name transformation, but it should be on by default in my opinion.
Issue Analytics
- State:
- Created 4 years ago
- Comments:15 (8 by maintainers)
If you plan to add an overload to HasCheckConstraint that doesn’t take a name, it would be great if you could also add an overload to HasDefaultValue that does take a name to align the two.
In SqlServer, Type1 names need to be unique across a schema not the entire database, the code below runs fine.
Supporting documentation - https://docs.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql