Passing a value of an incompatible type as a default value for an entity property or migration column can silently produce a broken schema, or fail
inconsistently depending on the database provider, because Entity Framework Core’s default-value APIs accept the value as object? and
give no compile-time type safety.
Why is this an issue?
Entity Framework Core’s default-value APIs — PropertyBuilder<T>.HasDefaultValue(), MigrationBuilder.AddColumn(),
MigrationBuilder.AlterColumn(), and ColumnsBuilder.Column() — all type their defaultValue/value
parameter as object?, never as the property’s or column’s actual .NET type, so the C# compiler cannot catch a mismatch: any value can be
passed, whether or not it matches the target type.
Entity Framework Core’s own runtime handling of a type-incompatible default value is neither complete nor consistent:
- Some mismatches, such as a
stringpassed for aGuidproperty, are caught immediately with a clearInvalidOperationExceptionthe first time the model is built. - Other mismatches, such as an
intpassed for astringproperty, pass model validation silently. - At the stage where the default value is turned into a SQL literal, behavior is provider-dependent: the same mismatch can pass on one provider
and fail on another. For example, adding an
intdefault for aGuidcolumn through a migration silently produces a schema with a bogus non-GUID literal default on SQLite, but fails outright when applied to SQL Server ("Operand type clash: int is incompatible with uniqueidentifier").
That same loosely typed default value is round-tripped through Entity Framework Core’s type-mapping machinery twice: once when the schema or migration is generated, and again when the database’s default is materialized back into the property while reading a row. Passing a mismatched type is inherently fragile: whether it happens to work today depends on the current database provider and Entity Framework Core version, and it can silently stop working after either changes, or after a schema or migration is regenerated.
What is the potential impact?
A type mismatch that is not caught by Entity Framework Core can produce a database column whose default value does not match its declared type. For
example, an int default value of 42 supplied for a string column can silently create the following schema, with
no error raised anywhere in the process:
"Label" TEXT NOT NULL DEFAULT '42'
Whether a given mismatch fails at write time, fails at read time, or does not fail at all, is provider-dependent and cannot be reliably predicted from the code alone.
How to fix it
Make sure the value passed as a default matches the .NET type of the target property or column. When in doubt, verify the resulting schema
explicitly, for example by inspecting a generated migration or reviewing context.Model, instead of relying on Entity Framework Core to
flag an incompatibility.
Code examples
Noncompliant code example
public void Configure(EntityTypeBuilder<Student> builder)
{
builder.Property(x => x.LastName) // LastName is a string property
.HasDefaultValue(42); // Noncompliant: int passed as default value for a string property
}
Compliant solution
public void Configure(EntityTypeBuilder<Student> builder)
{
builder.Property(x => x.LastName) // LastName is a string property
.HasDefaultValue("42");
}
Resources
Documentation
- Microsoft Learn - Entity Framework Core - Default Values
- Microsoft Learn - RelationalPropertyBuilderExtensions.HasDefaultValue Method
Related rules
- {rule:csharpsquid:S8718} - flags client-evaluated default values, such as
DateTime.Now, that should use database functions instead.