SQL column default value with Entity Framework

前端 未结 4 921
失恋的感觉
失恋的感觉 2020-12-17 10:09

I am trying to use Code-First EF6 with default SQL values.

For example, I have a \"CreatedDate\" column/property not null with a default in SQL of \"getdate()\"

相关标签:
4条回答
  • 2020-12-17 10:30

    Accepted answer is correct for EF6, I'm only adding EF Core solution; (also my solution focuses on changing the default-value, rather than creating it properly the first time)

    There is still no Data-Attribute in EF Core.

    And you must still use the Fluent API; it does have a HasDefaultValue

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .Property(b => b.Rating)
            .HasDefaultValue(3);
    }
    

    Note, there is also HasDefaultValueSql for NULL case:

            .HasDefaultValueSql("NULL");
    

    And you can also use the Migrations Up and Down methods, you can alter the defaultValue or defaultValueSql but you may need to drop Indexes first. Here's an example:

    public partial class RemovingDefaultToZeroPlantIdManualChange : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropIndex(
                name: "IX_TABLE_NAME_COLUMN_NAME",
                table: "TABLE_NAME"
            );
    
            migrationBuilder.AlterColumn<int>(
                name: "COLUMN_NAME",
                table: "TABLE_NAME",
                nullable: true,
                //note here, in the Up method, I'm specifying a new defaultValue:
                defaultValueSql: "NULL",
                oldClrType: typeof(int));
    
            migrationBuilder.CreateIndex(
                name: "IX_TABLE_NAME_COLUMN_NAME",
                table: "TABLE_NAME",
                column: "COLUMN_NAME"
            );
        }
    
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropIndex(
                name: "IX_TABLE_NAME_COLUMN_NAME",
                table: "TABLE_NAME"
            );
    
            migrationBuilder.AlterColumn<int>(
                name: "COLUMN_NAME",
                table: "TABLE_NAME",
                nullable: true,
                //note here, in the Down method, I'll restore to the old defaultValue:
                defaultValueSql: "0",
                oldClrType: typeof(int));
    
            migrationBuilder.CreateIndex(
                name: "IX_TABLE_NAME_COLUMN_NAME",
                table: "TABLE_NAME",
                column: "COLUMN_NAME"
            );
    
    
        }
    }
    
    0 讨论(0)
  • 2020-12-17 10:44

    [mysql]

    For those, who don't want to use computed and rewrite it after every db update, I wrote extension method for database partial class. Sure, there are thing that has to be improved or added, but for now it is enough for our using, enjoy.

    Take into account, that due to database_schema access it is not the fastest and also you need to have same entity name as table name (or rewrite it somehow).

        public static bool GetDBDefaults(object entity)
        {
            try
            {
                string table_name = entity.GetType().Name;
    
                string q = $"select column_name, column_default from information_schema.columns where column_default is not null and table_schema not in ('information_schema', 'sys', 'performance_schema', 'mysql') and table_name = '{table_name}' order by table_schema, table_name, ordinal_position;";
    
                List<DBDefaults> dbDefaults = new List<DBDefaults>();
                using (DatabaseModelFull db = new DatabaseModelFull())
                {
                    dbDefaults = db.Database.SqlQuery<DBDefaults>(q).ToList();
                }
    
                Type myType = entity.GetType();
                IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
                IList<FieldInfo> fields = new List<FieldInfo>(myType.GetFields());
    
                foreach (var dbDefault in dbDefaults)
                {
                    var prop = props.SingleOrDefault(x => x.Name == dbDefault.column_name);
    
                    if (prop != null)
                    {
                        if (dbDefault.column_default.Equals("CURRENT_TIMESTAMP"))
                            prop.SetValue(entity, System.Convert.ChangeType(DateTime.Now, prop.PropertyType));
                        else
                            prop.SetValue(entity, System.Convert.ChangeType(dbDefault.column_default, prop.PropertyType));
                        continue;
                    }
    
                    var field = fields.SingleOrDefault(x => x.Name == dbDefault.column_name);
    
                    if (field != null)
                    {
                        if (dbDefault.column_default.Equals("CURRENT_TIMESTAMP"))
                            field.SetValue(entity, System.Convert.ChangeType(DateTime.Now, field.FieldType));
                        else
                            field.SetValue(entity, System.Convert.ChangeType(dbDefault.column_default, field.FieldType));
                    }
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
    
    
        public class DBDefaults
        {
            public string column_name { get; set; }
            public string column_default { get; set; }
        }
    
    0 讨论(0)
  • 2020-12-17 10:44

    try with this. This code insert by default the current date

    //[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public DateTime Created { get; set; } = new DateTime();
    
    0 讨论(0)
  • 2020-12-17 10:46

    Currently in EF6 there is not an attribute to define database functions used for a certain property default value. You can vote on Codeplex to get it implemented:

    https://entityframework.codeplex.com/workitem/44

    The accepted way to implement something like that is to use Computed properties with Migrations where you specify the default database function.

    Your class could look like this in C#:

    public class MyEntity
    {
        [Key]
        public int Id { get; set; }
        public string Name { get; set; }
    
        [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
        public DateTime Created { get; set; }
    }
    

    The computed property doesn't have to be nullable.

    Then you have to run a migration and modify it by hand to include the default SQL function. A migration could look like:

    public partial class Initial : DbMigration
    {
        public override void Up()
        {
            CreateTable(
                "dbo.MyEntities",
                c => new
                    {
                        Id = c.Int(nullable: false, identity: true),
                        Name = c.String(),
                        Created = c.DateTime(nullable: false, defaultValueSql: "GetDate()"),
                    })
                .PrimaryKey(t => t.Id);
    
        }
    
        public override void Down()
        {
            DropTable("dbo.MyEntities");
        }
    }
    

    You will notice the defaultValueSql function. That is the key to get the computation working

    0 讨论(0)
提交回复
热议问题