实体框架6代码第一个默认值

mar*_*krk 187 entity-framework default-value ef-code-first

是否有"优雅"的方式给特定属性一个默认值?

也许通过DataAnnotations,类似于:

[DefaultValue("true")]
public bool Active { get; set; }
Run Code Online (Sandbox Code Playgroud)

谢谢.

dev*_*evi 159

您可以通过手动编辑代码首次迁移来实现:

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

  • 我不确定如果OP在创建`Event`对象时没有专门将`Active`设置为`true`,这将会起作用.对于不可为空的bool属性,默认值始终为"false",因此除非更改,否则这是实体框架将保存到db的内容.或者我错过了什么? (5认同)
  • @ GFoley83,是的,你是对的.此方法仅在数据库级别添加默认约束.对于完整的解决方案,您还需要在实体的构造函数中指定默认值或使用带有支持字段的属性,如上面的答案所示 (4认同)
  • AFAIK如果重新迁移迁移,您的手动更改将会丢失 (2认同)

rav*_*nsp 66

已经有一段时间了,但给其他人留了一张便条.我实现了属性所需的功能,并根据需要使用该属性修饰了我的模型类字段.

[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }
Run Code Online (Sandbox Code Playgroud)

得到了这两篇文章的帮助:

我做了什么:

定义属性

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SqlDefaultValueAttribute : Attribute
{
    public string DefaultValue { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在"OnModelCreating"的上下文中

modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));
Run Code Online (Sandbox Code Playgroud)

在自定义SqlGenerator中

private void SetAnnotatedColumn(ColumnModel col)
{
    AnnotationValues values;
    if (col.Annotations.TryGetValue("SqlDefaultValue", out values))
    {
         col.DefaultValueSql = (string)values.NewValue;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在迁移配置构造函数中,注册自定义SQL生成器.

SetSqlGenerator("System.Data.SqlClient", new HarmonyMigrationSqlGenerator());
Run Code Online (Sandbox Code Playgroud)

  • 您甚至可以在全局范围内执行此操作,而无需在每个实体上放置`[SqlDefaultValue(DefaultValue ="getutcdate()")]`.1)简单地删除`modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention <SqlDefaultValueAttribute,string>("SqlDefaultValue",(p,attributes)=> attributes.Single().DefaultValue));`2)添加`modelBuilder.Properties() .Where(x => x.PropertyType == typeof(DateTime)).Configure(c => c.HasColumnType("datetime2").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed).HasColumnAnnotation("SqlDefaultValue","getdate()") );` (6认同)
  • 自定义SqlGenerator来自这里:https://andy.mehalick.com/2014/02/06/ef6-adding-a-created-datetime-column-automatically-with-code-first-migrations/ (3认同)
  • 请问自定义 SqlGenerator 在哪里? (2认同)

Jev*_*nko 66

上述答案确实有所帮助,但只提供了部分解决方案.主要问题是,只要删除"默认值"属性,就不会删除数据库中列的约束.因此,之前的默认值仍将保留在数据库中.

以下是该问题的完整解决方案,包括删除属性删除时的SQL约束.我也在重用.NET Framework的原生DefaultValue属性.

用法

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue("getutcdate()")]
public DateTime CreatedOn { get; set; }
Run Code Online (Sandbox Code Playgroud)

为此,您需要更新IdentityModels.csConfiguration.cs文件

IdentityModels.cs文件

在您的ApplicationDbContext课程中添加/更新此方法

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
            base.OnModelCreating(modelBuilder);
            var convention = new AttributeToColumnAnnotationConvention<DefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.SingleOrDefault().Value.ToString());
            modelBuilder.Conventions.Add(convention);
}
Run Code Online (Sandbox Code Playgroud)

Configuration.cs文件

Configuration通过注册自定义Sql生成器来更新类构造函数,如下所示:

internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
    public Configuration()
    {
        // DefaultValue Sql Generator
        SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来,添加自定义Sql生成器类(您可以将其添加到Configuration.cs文件或单独的文件)

internal class DefaultValueSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    private int dropConstraintCount = 0;

    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)
    {
        AnnotationValues values;
        if (column.Annotations.TryGetValue("SqlDefaultValue", out 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<ColumnModel> columns, string tableName)
    {
        foreach (var column in columns)
        {
            SetAnnotatedColumn(column, tableName);
        }
    }

    private string GetSqlDropConstraintQuery(string tableName, string columnName)
    {
        var tableNameSplittedByDot = tableName.Split('.');
        var tableSchema = tableNameSplittedByDot[0];
        var tablePureName = tableNameSplittedByDot[1];

        var str = $@"DECLARE @var{dropConstraintCount} nvarchar(128)
SELECT @var{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 @var{dropConstraintCount} IS NOT NULL
    EXECUTE('ALTER TABLE {tableSchema}.[{tablePureName}] DROP CONSTRAINT [' + @var{dropConstraintCount} + ']')";

        dropConstraintCount = dropConstraintCount + 1;
        return str;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这种方法对我来说非常合适.我做的一个改进是使用相同的逻辑覆盖Generate(CreateTableOperation createTableOperation)和Generate(AddColumnOperation addColumnOperation),以便捕获这些场景.我也只检查values.NewValue为null,因为我希望我的默认值为空字符串. (2认同)
  • @Delorian我已经更新了我的回答,谢谢你的评论 (2认同)

cal*_*oyd 26

您的模型属性不必是"自动属性"即使这更容易.DefaultValue属性实际上只是提供信息的元数据这里接受的答案是构造函数方法的一种替代方法.

public class Track
{

    private const int DEFAULT_LENGTH = 400;
    private int _length = DEFAULT_LENGTH;
    [DefaultValue(DEFAULT_LENGTH)]
    public int LengthInMeters {
        get { return _length; }
        set { _length = value; }
    }
}
Run Code Online (Sandbox Code Playgroud)

public class Track
{
    public Track()
    {
        LengthInMeters = 400;   
    }

    public int LengthInMeters { get; set; }        
}
Run Code Online (Sandbox Code Playgroud)

这仅适用于使用此特定类创建和使用数据的应用程序.如果数据访问代码是集中的,通常这不是问题.要更新所有应用程序的值,您需要配置数据源以设置默认值.Devi的答案显示了如何使用迁移,sql或您的数据源所说的任何语言来完成它.

  • 注意:这不会在数据库*中设置默认值*.不使用您的实体的其他程序将不会获得该默认值. (20认同)
  • 在某些情况下,我遇到了构造函数方法的问题; 在支持字段中设置默认值似乎是侵入性最小的解决方案. (2认同)

Sam*_*bes 11

我做了什么,我在实体的构造函数中初始化了值

注意:DefaultValue属性不会自动设置属性的值,您必须自己完成

  • 设置值的构造函数的问题是EF将在提交事务时对数据库进行更新. (4认同)

Roa*_*tad 9

在 .NET Core 3.1 中,您可以在模型类中执行以下操作:

    public bool? Active { get; set; } 
Run Code Online (Sandbox Code Playgroud)

在 DbContext OnModelCreating 中添加默认值。

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Foundation>()
            .Property(b => b.Active)
            .HasDefaultValueSql("1");

        base.OnModelCreating(modelBuilder);
    }
Run Code Online (Sandbox Code Playgroud)

在数据库中得到以下结果

在此输入图像描述

注意:如果您的属性没有可为空(bool?),您将收到以下警告

The 'bool' property 'Active' on entity type 'Foundation' is configured with a database-generated default. This default will always be used for inserts when the property has the value 'false', since this is the CLR default for the 'bool' type. Consider using the nullable 'bool?' type instead so that the default will only be used for inserts when the property value is 'null'.
Run Code Online (Sandbox Code Playgroud)

  • OP 的问题是关于 EF6,而不是 EF Core。 (3认同)

Ant*_*ggs 8

我承认我的方法避开了整个“代码优先”的概念。但是如果你有能力改变表本身的默认值......它比你必须在上面经历的长度简单得多......我只是懒得做所有这些工作!

似乎海报最初的想法似乎可行:

[DefaultValue(true)]
public bool IsAdmin { get; set; }
Run Code Online (Sandbox Code Playgroud)

我以为他们只是犯了添加引号的错误……但可惜没有这样的直觉。其他建议对我来说太多了(授予我进入表格并进行更改所需的特权......并不是每个开发人员在每种情况下都会这样做)。最后,我只是按照老式的方式做到了。我在 SQL Server 表中设置了默认值......我的意思是真的,已经足够了!注意:我进一步测试了添加迁移和更新数据库,并且更改卡住了。 在此处输入图片说明


Den*_*uig 7

在@SedatKapanoglu评论之后,我添加了我的所有方法,因为他是对的,只是使用流畅的API不起作用.

1-创建自定义代码生成器并覆盖为ColumnModel生成.

   public class ExtendedMigrationCodeGenerator : CSharpMigrationCodeGenerator
{

    protected override void Generate(ColumnModel column, IndentedTextWriter writer, bool emitName = false)
    {

        if (column.Annotations.Keys.Contains("Default"))
        {
            var value = Convert.ChangeType(column.Annotations["Default"].NewValue, column.ClrDefaultValue.GetType());
            column.DefaultValue = value;
        }


        base.Generate(column, writer, emitName);
    }

}
Run Code Online (Sandbox Code Playgroud)

2-分配新代码生成器:

public sealed class Configuration : DbMigrationsConfiguration<Data.Context.EfSqlDbContext>
{
    public Configuration()
    {
        CodeGenerator = new ExtendedMigrationCodeGenerator();
        AutomaticMigrationsEnabled = false;
    }
}
Run Code Online (Sandbox Code Playgroud)

3-使用流畅的api创建注释:

public static void Configure(DbModelBuilder builder){    
builder.Entity<Company>().Property(c => c.Status).HasColumnAnnotation("Default", 0);            
}
Run Code Online (Sandbox Code Playgroud)


小智 5

这很简单!只需用 required 进行注释。

[Required]
public bool MyField { get; set; }
Run Code Online (Sandbox Code Playgroud)

由此产生的迁移将是:

migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);
Run Code Online (Sandbox Code Playgroud)

如果要true,请在更新数据库之前将迁移中的defaultValue 更改为true

  • 如果我们需要默认值为“true”怎么办? (2认同)