64

I am using EF6 for storing instances of the report class in my database. The database already contains data. Say I wanted to add a property to report,

public class report {
    // ... some previous properties

    // ... new property:
    public string newProperty{ get; set; }
}

Now if I go to the package-manager console and execute

add-migration Report-added-newProperty
update-database

I will get a file in the '/Migrations' folder adding a newProperty column to the table. This works fine. However, on the older entries in the database, the value for the newProperty is now an empty string. But I want it to be, e.g., "old".

So my question is: How do I set default values for new properties (of any type) in the migration script (or elsewhere)?

Noctis
  • 11,175
  • 3
  • 40
  • 79
EluciusFTW
  • 2,405
  • 6
  • 39
  • 56
  • 1
    The behavior here is not due to entity framework but TSQL. When you add a new nullable column in TSQL then one must use `WITH VALUES` to specify the value of existing records (see this [question](https://stackoverflow.com/questions/92082/add-a-column-with-a-default-value-to-an-existing-table-in-sql-server)). The workaround given in answers here is to make the column non-nullable. Another alternative is to define a custom DefaultValue attribute such as [this](https://stackoverflow.com/a/34894274/5301903). – Hans Vonn Mar 13 '18 at 22:14

5 Answers5

119

If you see the generated migration code you will see AddColumn

AddColumn("dbo.report", "newProperty", c => c.String(nullable: false));

You can add defaultValue

AddColumn("dbo.report", "newProperty", 
           c => c.String(nullable: false, defaultValue: "old"));

Or add defaultValueSql

AddColumn("dbo.report", "newProperty",
           c => c.String(nullable: false, defaultValueSql: "GETDATE()"));
Christoph Fink
  • 22,099
  • 9
  • 67
  • 105
Hamid Pourjam
  • 19,792
  • 9
  • 57
  • 71
  • 22
    Would be nice if it properly pulled in the [DefaultValue(xyz)] attribute as defined in the code first model. Instead of having to remember which columns need what each time you Add-Migration; having to post-edit the generated file, followed by the normal running of the Update-Database against that changed file. I wonder if it's possible to override that unintelligent behavior.... I can't believe no one has improved on this. Surely I'm not the only one frustrated by this with the default AspNet Identity model... LockoutEnabled, EmailConfirmed, AccessFailedCount. all hardcoded, all problems – Barry Apr 02 '17 at 08:42
  • 3
    This does not seem to work with MySQL-EF. 'defaultValue' doesn't set the value, and 'defaultValueSql' creates an exception ("blob text geometry or json column can't have a default value") ... the only workaround left is to use raw SQL. :( – Efrain Nov 06 '17 at 12:56
  • For those that needed default value for an enum just like me: AddColumn("dbo.report", "newEnumProperty", c => c.Int(nullable: false, defaultValue: 0)); //use integer to set default enum value – EeKay Jun 13 '18 at 10:07
  • 1
    Quoting @Efrain, with EF Core 5 and MySQL I got empty fields. The default is not applied. – StefanoV Dec 16 '20 at 09:50
  • 2
    How to add default value of other column, eg: I want the new property default vaue to be report.OldProperty ? – user1735921 Apr 10 '21 at 19:10
16

Hope it helps someone. Putting everything together from previous answers (example using a boolean property):

1) Add a new property to the entity.

/// <summary>
/// Determines if user is enabled or not. Default value is true
/// </summary>
public bool IsEnabled { get; set; }

2) Run the command below to add the new change in the migrations.

add-migration addIsEnabledColumn

3) A migration file is created from the command above, open that file.

enter image description here

4) Set the default value.

public override void Up()
{
        AddColumn(
            "dbo.AspNetUsers", 
            "IsEnabled", 
            c => c.Boolean(
                nullable: false, 
                defaultValue: true
            )
        );
}
Morteza Jalambadani
  • 2,016
  • 6
  • 21
  • 33
Victor LG
  • 506
  • 5
  • 7
  • 3
    5) Run Update-Database after step 4 to update the actual database too with the changes. Adding this point just for the sake of newbies. – vinmm Jun 11 '20 at 07:20
  • Is it possible to have the default be a function of other fields? – RJFalconer Sep 03 '21 at 20:28
7

You have to change the line in your migration script which adds the the property/column like this:

AddColumn("dbo.reports", "newProperty", c => c.String(nullable: false, defaultValue: "test"));
ngu
  • 834
  • 1
  • 9
  • 22
-1

I have resolved this problem by overriding the SaveChanges method. See below for my solution.

  1. Solution Explain

    i) Override the SaveChanges method in the DbContext class.

    public override int SaveChanges()
    {
        return base.SaveChanges();
    }
    

    ii) Write logic to set default values

    public override int SaveChanges()
    {
        //set default value for your property
        foreach (var entry in ChangeTracker.Entries().Where(entry => entry.Entity.GetType().GetProperty("YOUR_PROPERTY") != null))
        {
            if (entry.State == EntityState.Added)
            {
                if (entry.Property("YOUR_PROPERTY").CurrentValue == null)
                    entry.Property("YOUR_PROPERTY").CurrentValue = YOUR_DEFAULT_VALUE;
            }
        }
    
        return base.SaveChanges();
    }
    
  2. Example

    public override int SaveChanges()
    {
        //set default value for RegistedDate property
        foreach (var entry in ChangeTracker.Entries().Where(entry => entry.Entity.GetType().GetProperty("RegistedDate") != null))
        {
            if (entry.State == EntityState.Added)
            {
                if ((DateTime)entry.Property("RegistedDate").CurrentValue == DateTime.MinValue)
                    entry.Property("RegistedDate").CurrentValue = DateTime.Now;
            }
        }
    
        //set default value for IsActive property
        foreach (var entry in ChangeTracker.Entries().Where(entry => entry.Entity.GetType().GetProperty("IsActive") != null))
        {
            if (entry.State == EntityState.Added)
            {
                if(entry.Property("IsActive").CurrentValue == null)
                    entry.Property("IsActive").CurrentValue = false;
            }
        }
    
        return base.SaveChanges();
    }
    
shalitha senanayaka
  • 1,447
  • 14
  • 29
-6

I found that just using Auto-Property Initializer on entity property is enough to get the job done.

For example:

public class Thing {
    public bool IsBigThing { get; set; } = false;
}
Velyo
  • 224
  • 1
  • 8