Skip to content

Settings.GenerateHasDefaultValueSql

Simon Hughes edited this page Sep 29, 2026 · 4 revisions

Puts each column's SQL default into the EF model with .HasDefaultValueSql(), so EF knows about it rather than just your POCO.

Type bool
Default false
Applies to EF Core only
Databases All
In Database.tt? Yes

What it does

A column default can live in two places in the generated code, and they are independent:

  • In the POCO, as a constructor assignment or property initialiser. That is Settings.IncludeColumnsWithDefaults, which is on by default.
  • In the EF model, as .HasDefaultValueSql("...") in the configuration. That is this setting, which is off by default.

The second tells EF Core the database has a default. That matters for migrations, for ValueGeneratedOnAdd inference, and for anything that reflects over the model.

Example

Product.UnitPrice is declared decimal(18, 2) NOT NULL CONSTRAINT DF_Product_UnitPrice DEFAULT ((0)).

Settings.GenerateHasDefaultValueSql = false (default)

    // Product
    public class ProductConfiguration : IEntityTypeConfiguration<Product>
    {
        public void Configure(EntityTypeBuilder<Product> builder)
        {
            builder.ToTable("Product", "dbo");
            builder.HasKey(x => x.ProductId);

            builder.Property(x => x.ProductId).HasColumnName(@"ProductId").HasColumnType("int").IsRequired().ValueGeneratedOnAdd().UseIdentityColumn();
            builder.Property(x => x.ProductName).HasColumnName(@"ProductName").HasColumnType("nvarchar(100)").IsRequired().HasMaxLength(100);
            builder.Property(x => x.UnitPrice).HasColumnName(@"UnitPrice").HasColumnType("decimal(18,2)").HasPrecision(18,2).IsRequired();
            builder.Property(x => x.Notes).HasColumnName(@"Notes").HasColumnType("nvarchar(max)").IsRequired(false);
            builder.Property(x => x.CategoryId).HasColumnName(@"CategoryId").HasColumnType("int").IsRequired();
            builder.Property(x => x.DisplayLabel).HasColumnName(@"DisplayLabel").HasColumnType("nvarchar(150)").IsRequired(false).HasMaxLength(150).ValueGeneratedOnAddOrUpdate();

            // Foreign keys
            builder.HasOne(a => a.Category).WithMany(b => b.Products).HasForeignKey(c => c.CategoryId).OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FK_Product_Category");
        }
    }

Settings.GenerateHasDefaultValueSql = true

    // Product
    public class ProductConfiguration : IEntityTypeConfiguration<Product>
    {
        public void Configure(EntityTypeBuilder<Product> builder)
        {
            builder.ToTable("Product", "dbo");
            builder.HasKey(x => x.ProductId);

            builder.Property(x => x.ProductId).HasColumnName(@"ProductId").HasColumnType("int").IsRequired().ValueGeneratedOnAdd().UseIdentityColumn();
            builder.Property(x => x.ProductName).HasColumnName(@"ProductName").HasColumnType("nvarchar(100)").IsRequired().HasMaxLength(100);
            builder.Property(x => x.UnitPrice).HasColumnName(@"UnitPrice").HasColumnType("decimal(18,2)").HasPrecision(18,2).IsRequired().HasDefaultValueSql(@"0");
            builder.Property(x => x.Notes).HasColumnName(@"Notes").HasColumnType("nvarchar(max)").IsRequired(false);
            builder.Property(x => x.CategoryId).HasColumnName(@"CategoryId").HasColumnType("int").IsRequired();
            builder.Property(x => x.DisplayLabel).HasColumnName(@"DisplayLabel").HasColumnType("nvarchar(150)").IsRequired(false).HasMaxLength(150).ValueGeneratedOnAddOrUpdate();

            // Foreign keys
            builder.HasOne(a => a.Category).WithMany(b => b.Products).HasForeignKey(c => c.CategoryId).OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FK_Product_Category");
        }
    }

The raw SQL default is emitted verbatim, brackets and all, exactly as the database reports it.

When to use it

You are moving to migrations. Scaffolding an initial migration from a reverse-engineered model produces an AlterColumn for every defaulted column unless the model knows the defaults are already there. Turning this on makes the initial migration empty, which is the whole objective - see Migrations.

Something reflects over the EF model to build documentation, an admin UI or a schema comparison. The defaults are only visible there if this is on.

Leave it off if the generated code is a read model over a database somebody else owns. The defaults are then not your business and the extra fluent calls are noise.

Gotchas

Expression defaults are always emitted, regardless of this setting. A default that is SQL rather than a literal - NEXT VALUE FOR [dbo].[MySequence], SUSER_SNAME(), CURRENT_USER, now(), SYSDATE + 30 - has no C# value the generator could put in the POCO, so the column gets .HasDefaultValueSql() whether this is on or off. That is what makes EF Core leave the column out of the INSERT and let the database run the expression. The exceptions are the few functions with a C# equivalent, such as getutcdate(), which are set in the POCO instead - see Settings.IncludeColumnsWithDefaults. So you will see the call on some columns with the setting off, which looks like a bug and is not.

The SQL is copied as the database reports it and is dialect-specific. Only a pair of outer brackets that encloses the whole default is removed, which is why ((0)) above became 0. getutcdate() is SQL Server; now() is PostgreSQL. The generated model is not portable between providers, which only matters if you were hoping it would be.

Turning it on can conflict with the POCO default. With both this and IncludeColumnsWithDefaults on, the value is set in C# and declared in the model. EF then sends the C# value on insert, so the database default never fires. Harmless when they agree, confusing when they drift.

EF Core only. On EF 6 the setting is read and ignored.

Computed columns are not defaults. They are handled by Settings.UsePrivateSetterForComputedColumns and are always emitted as ValueGeneratedOnAddOrUpdate().

See also

Clone this wiki locally