Entity Framework 6 Code first 默认值

16 浏览
0 Comments

Entity Framework 6 Code first 默认值

有没有一种"优雅"的方法为特定的属性设置默认值?

也许可以通过 DataAnnotations 这样的方式实现:

[DefaultValue("true")]
public bool Active { get; set; }

谢谢。

0
0 Comments

在使用Entity Framework 6 Code First时,如果要移除属性上的Default value特性,会导致数据库中的列约束不会被移除,从而导致之前的默认值仍然存在于数据库中。以下是解决该问题的完整方案,包括移除属性时的SQL约束。

首先,在类中使用以下特性和属性来定义默认值:

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue("getutcdate()")]
public DateTime CreatedOn { get; set; }

然后,在`IdentityModels.cs`文件中添加或更新`ApplicationDbContext`类中的方法:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    var convention = new AttributeToColumnAnnotationConvention("SqlDefaultValue", (p, attributes) => attributes.SingleOrDefault().Value.ToString());
    modelBuilder.Conventions.Add(convention);
}

接下来,在`Configuration.cs`文件中,更新`Configuration`类的构造函数,注册自定义的Sql生成器:

internal sealed class Configuration : DbMigrationsConfiguration
{
    public Configuration()
    {
        // DefaultValue Sql Generator
        SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
    }
}

然后,添加自定义的Sql生成器类:

internal class DefaultValueSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    private int dropConstraintCount;
    protected override void Generate(AddColumnOperation addColumnOperation)
    {
        SetAnnotatedColumn(addColumnOperation.Column, addColumnOperation.Table);
        base.Generate(addColumnOperation);
    }
    protected override void Generate(AlterColumnOperation alterColumnOperation)
    {
        SetAnnotatedColumn(alterColumnOperation.Column, alterColumnOperation.Table);
        base.Generate(alterColumnOperation);
    }
    protected override void Generate(CreateTableOperation createTableOperation)
    {
        SetAnnotatedColumns(createTableOperation.Columns, createTableOperation.Name);
        base.Generate(createTableOperation);
    }
    protected override void Generate(AlterTableOperation alterTableOperation)
    {
        SetAnnotatedColumns(alterTableOperation.Columns, alterTableOperation.Name);
        base.Generate(alterTableOperation);
    }
    private void SetAnnotatedColumn(ColumnModel column, string tableName)
    {
        if (column.Annotations.TryGetValue("SqlDefaultValue", out var values))
        {
            if (values.NewValue == null)
            {
                column.DefaultValueSql = null;
                using var writer = Writer();
                // Drop Constraint
                writer.WriteLine(GetSqlDropConstraintQuery(tableName, column.Name));
                Statement(writer);
            }
            else
            {
                column.DefaultValueSql = (string)values.NewValue;
            }
        }
    }
    private void SetAnnotatedColumns(IEnumerable columns, string tableName)
    {
        foreach (var column in columns)
        {
            SetAnnotatedColumn(column, tableName);
        }
    }
    private string GetSqlDropConstraintQuery(string tableName, string columnName)
    {
        var tableNameSplitByDot = tableName.Split('.');
        var tableSchema = tableNameSplitByDot[0];
        var tablePureName = tableNameSplitByDot[1];
        var str = $@"DECLARE {dropConstraintCount} nvarchar(128)
SELECT {dropConstraintCount} = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'{tableSchema}.[{tablePureName}]')
AND col_name(parent_object_id, parent_column_id) = '{columnName}';
IF {dropConstraintCount} IS NOT NULL
EXECUTE('ALTER TABLE {tableSchema}.[{tablePureName}] DROP CONSTRAINT [' + {dropConstraintCount} + ']')";
        dropConstraintCount++;
        return str;
    }
}

这样就解决了在移除属性上的Default value特性时,数据库中的列约束仍然存在的问题。

0
0 Comments

Entity Framework 6 Code First 默认值问题的原因是,在使用Code First迁移时,如果不显式设置属性的默认值,Entity Framework会将非空布尔属性的默认值设置为false。这可能导致在数据库中保存不正确的默认值。解决这个问题的方法是在迁移代码中手动设置默认值,或者在实体的构造函数中设置默认值。

以下是解决方法的代码示例:

public override void Up()
{    
   AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
}

上述代码将在数据库层级添加默认约束。为了完整的解决方案,还需要在实体的构造函数中分配默认值,或者使用带有后备字段的属性。

对于基本类型,可以使用`defaultValueSql`而不是`defaultValue`来设置默认值。例如,对于`DATETIMEOFFSET`类型,可以使用`defaultValueSql: "SYSDATETIMEOFFSET"`。

对于希望了解如何添加迁移并在哪里放置代码的人,可以参考这个链接:[msdn.microsoft.com/en-us/library/jj591621(v=vs.113).aspx](https://msdn.microsoft.com/en-us/library/jj591621(v=vs.113).aspx),具体参考其中的“Data Motion / Custom SQL”部分。

需要注意的是,由于这个答案比较老,使用之前应该先尝试查看更近期的解决方案。如果有人对此答案有异议,请提出理由。

需要注意的是,如果重新生成迁移,手动更改将会丢失。

如果想要移除默认值,可以首先编写迁移代码以在数据库层级删除默认值,然后更改数据访问层的映射。

0
0 Comments

问题:Entity Framework 6 Code first中如何设置默认值?

原因:在Entity Framework 6 Code first中,默认情况下无法直接设置数据库表的默认值。然而,通过使用自定义的属性、自定义的SQL生成器以及自定义的迁移配置,可以实现设置默认值的功能。

解决方法:

1. 定义一个名为SqlDefaultValueAttribute的属性,用于标记需要设置默认值的属性。

2. 在DbContext的OnModelCreating方法中,使用modelBuilder.Conventions.Add方法将自定义的AttributeToColumnAnnotationConvention注册到EF中,以便在生成数据库表时能够读取属性上的SqlDefaultValueAttribute。

3. 自定义一个SQL生成器,并在其中编写逻辑,使之能够根据属性上的SqlDefaultValueAttribute来设置数据库表的默认值。

4. 在迁移配置中,将自定义的SQL生成器注册到EF中,以便在生成迁移脚本时能够使用自定义的SQL生成逻辑。

代码如下:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SqlDefaultValueAttribute : Attribute
{
    public string DefaultValue { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));
}
private void SetAnnotatedColumn(ColumnModel col)
{
    AnnotationValues values;
    if (col.Annotations.TryGetValue("SqlDefaultValue", out values))
    {
         col.DefaultValueSql = (string)values.NewValue;
    }
}
public class CustomMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate(AddColumnOperation addColumnOperation)
    {
        base.Generate(addColumnOperation);
        SetAnnotatedColumn(addColumnOperation.Column);
    }
}
public Configuration()
{
    SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator());
}

另外,在使用Entity Framework Core时,可以通过以下代码实现相同的功能:

modelBuilder.Properties().Where(x => x.PropertyType == typeof(DateTime)).Configure(c => c.HasColumnType("datetime2").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed).HasColumnAnnotation("SqlDefaultValue", "getdate()"));

需要注意的是,当使用EF进行插入操作时,模型对象属性中设置的值会覆盖数据库表中的默认值,因此设置数据库表的默认值在这种情况下是无效的。

0