有没有一种“优雅”的方法为特定属性赋予默认值?
也许可以通过 DataAnnotations 来实现,类似于:
[DefaultValue("true")]
public bool Active { get; set; }
谢谢。
有没有一种“优雅”的方法为特定属性赋予默认值?
也许可以通过 DataAnnotations 来实现,类似于:
[DefaultValue("true")]
public bool Active { get; set; }
谢谢。
您可以通过手动编辑代码来执行第一个迁移操作:
public override void Up()
{
AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
}
好久不见,为了其他人留个便条。 我使用一个属性实现了所需功能,并将该属性装饰到我的模型类字段上,以达到我想要的效果。
[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }
得到这2篇文章的帮助:
我所做的:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SqlDefaultValueAttribute : Attribute
{
public string DefaultValue { get; set; }
}
在上下文的"OnModelCreating"中。modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("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;
}
}
然后在迁移配置的构造函数中,注册自定义的 SQL 生成器。
SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator());
[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()"));
- Daniel Skowroński以上回答确实有所帮助,但只提供了部分解决方案。 主要问题是,一旦删除默认值属性,数据库中列上的约束将不会被删除。因此,以前的默认值仍将保留在数据库中。
下面是解决问题的完整方案,包括删除属性后在 SQL 约束上的操作。
我还重新使用了 .NET Framework 的本机 DefaultValue
属性。
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue("getutcdate()")]
public DateTime CreatedOn { get; set; }
为了使此操作生效,您需要更新 IdentityModels.cs 和 Configuration.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);
}
通过注册自定义的Sql生成器,更新您的Configuration
类构造函数,如下所示:
internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
public Configuration()
{
// DefaultValue Sql Generator
SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
}
}
接下来,添加自定义 Sql 生成器类(可以将其添加到 Configuration.cs 文件或单独的文件中)
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<ColumnModel> 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 @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++;
return str;
}
}
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
属性吗?如果需要,为什么?在我的测试中,似乎不设置它也没有影响。 - RamNow你的模型属性不一定要是“自动属性”,尽管这样更容易。而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; }
}
}
对比。
public class Track
{
public Track()
{
LengthInMeters = 400;
}
public int LengthInMeters { get; set; }
}
仅适用于使用此特定类创建和使用数据的应用程序。通常,如果数据访问代码是集中的,则这不是问题。要在所有应用程序中更新该值,您需要配置数据源以设置默认值。Devi的答案展示了如何使用迁移、SQL或任何语言来完成。
我在实体的构造函数中初始化了值。
注意:DefaultValue属性不会自动设置属性的值,您必须自己设置。
似乎原帖作者的想法几乎可以奏效:
[DefaultValue(true)]
public bool IsAdmin { get; set; }
在 @SedatKapanoglu 的评论之后,我添加了自己的方法,因为他是对的,仅使用流畅的API是不起作用的。
1- 创建自定义代码生成器并覆盖 ColumnModel 的 Generate 方法。
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);
}
}
2- 分配新的代码生成器:
public sealed class Configuration : DbMigrationsConfiguration<Data.Context.EfSqlDbContext>
{
public Configuration()
{
CodeGenerator = new ExtendedMigrationCodeGenerator();
AutomaticMigrationsEnabled = false;
}
}
3- 使用流畅的 API 创建注释:
public static void Configure(DbModelBuilder builder){
builder.Entity<Company>().Property(c => c.Status).HasColumnAnnotation("Default", 0);
}
public bool? Active { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Foundation>()
.Property(b => b.Active)
.HasDefaultValueSql("1");
base.OnModelCreating(modelBuilder);
}
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'.
[Required]
public bool MyField { get; set; }
迁移的结果将是:
migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);
如果你想要真实的结果,请在更新数据库之前在迁移中将defaultValue更改为true
true
怎么办? - Christos LytrasNotMyField
/s - devklickpublic class Thing {
public bool IsBigThing{ get; set; } = false;
}
this.Active = true;
? 我认为在获取时 DB 值将具有优先权,但是如果首先 new 然后附加实体进行更新而没有先获取,则要小心,因为更改跟踪 可能 会将其视为您想要更新该值的操作。 评论一下,因为我很久没有使用 EF 了,感觉这只是瞎猜。 - AaronLSpublic bool Inactive { get; set; }
- Jesse Hufstetler