有没有“优雅”的方法来赋予特定属性默认值?
也许是通过DataAnnotations,类似:
[DefaultValue("true")]
public bool Active { get; set; }
谢谢。
public bool Inactive { get; set; }
😉
有没有“优雅”的方法来赋予特定属性默认值?
也许是通过DataAnnotations,类似:
[DefaultValue("true")]
public bool Active { get; set; }
谢谢。
public bool Inactive { get; set; }
😉
Answers:
您可以通过手动编辑代码优先迁移来做到这一点:
public override void Up()
{
AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
}
Active
为OP,那么这是否会起作用。默认值始终位于不可为null的bool属性上,因此,除非更改,否则这是实体框架将保存到数据库的内容。还是我错过了什么?true
Event
false
已经有一段时间了,但是给其他人留下了笔记。我通过属性实现了所需的功能,并根据需要用该属性装饰了模型类字段。
[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }
得到了这两篇文章的帮助:
我做了什么:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SqlDefaultValueAttribute : Attribute
{
public string DefaultValue { get; set; }
}
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()"));
以上答案确实有帮助,但仅提供了部分解决方案。主要问题是,一旦删除“默认值”属性,就不会删除对数据库中列的约束。因此,以前的默认值仍将保留在数据库中。
这是该问题的完整解决方案,包括删除属性删除方面的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);
}
Configuration
通过注册自定义Sql生成器来更新类构造函数,如下所示:
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 = 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;
}
}
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
属性吗?如果是这样,为什么?在我的测试中,将其遗漏似乎没有任何效果。
您的模型属性不必是“自动属性”,尽管这很容易。而且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或数据源使用的任何语言来完成此任务。
@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);
}
这很简单!只是用必需的注释。
[Required]
public bool MyField { get; set; }
最终的迁移将是:
migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);
如果要为true,请在迁移之前将defaultValue更改为true,然后再更新数据库
true
怎么办?
我承认我的方法避开了整个“代码优先”的概念。但是,如果您能够更改表本身的默认值,那么它比上面必须经过的长度要简单得多...我懒得做所有这些工作!
似乎海报的原始想法会起作用:
[DefaultValue(true)]
public bool IsAdmin { get; set; }
我以为他们只是犯了加引号的错误...但是可惜没有这么直观。其他建议对我来说太过分了(授予我拥有进入表中进行更改所需的特权...并不是每个开发人员都会在每种情况下使用)。最后,我只是按照老式的方式做了。我在SQL Server表中设置了默认值...我的意思是,已经足够了!注意:我进一步测试了添加迁移和更新数据库以及所做的更改。
只需重载Model类的默认构造函数,并传递您可能会或可能不会使用的任何相关参数。这样,您可以轻松提供属性的默认值。下面是一个例子。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Aim.Data.Domain
{
[MetadataType(typeof(LoginModel))]
public partial class Login
{
public Login(bool status)
{
this.CreatedDate = DateTime.Now;
this.ModifiedDate = DateTime.Now;
this.Culture = "EN-US";
this.IsDefaultPassword = status;
this.IsActive = status;
this.LoginLogs = new HashSet<LoginLog>();
this.LoginLogHistories = new HashSet<LoginLogHistory>();
}
}
public class LoginModel
{
[Key]
[ScaffoldColumn(false)]
public int Id { get; set; }
[Required]
public string LoginCode { get; set; }
[Required]
public string Password { get; set; }
public string LastPassword { get; set; }
public int UserGroupId { get; set; }
public int FalseAttempt { get; set; }
public bool IsLocked { get; set; }
public int CreatedBy { get; set; }
public System.DateTime CreatedDate { get; set; }
public Nullable<int> ModifiedBy { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public string Culture { get; set; }
public virtual ICollection<LoginLog> LoginLogs { get; set; }
public virtual ICollection<LoginLogHistory> LoginLogHistories { get; set; }
}
}
让我们考虑一下,您有一个名为Products的类名,并且有一个IsActive字段。只需要一个create构造函数:
Public class Products
{
public Products()
{
IsActive = true;
}
public string Field1 { get; set; }
public string Field2 { get; set; }
public bool IsActive { get; set; }
}
然后,您的IsActive默认值为True!
编辑:
如果要使用SQL进行此操作,请使用以下命令:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.IsActive)
.HasDefaultValueSql("true");
}
在.NET Core 3.1中,您可以在模型类中执行以下操作:
public bool? Active { get; set; }
在DbContext OnModelCreating中添加默认值。
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'.
我发现仅对实体属性使用自动属性初始化程序就足以完成工作。
例如:
public class Thing {
public bool IsBigThing{ get; set; } = false;
}
嗯...我先做数据库,在这种情况下,这实际上要容易得多。EF6对吗?只需打开模型,右键单击要为其设置默认值的列,选择属性,您将看到“ DefaultValue”字段。只需填写并保存。它将为您设置代码。
不过,您的里程可能会因代码而异,但我还没有处理过。
许多其他解决方案的问题在于,尽管它们最初可能会起作用,但是一旦您重建模型,它就会丢弃您插入到计算机生成的文件中的所有自定义代码。
该方法通过向edmx文件添加一个额外的属性来工作:
<EntityType Name="Thingy">
<Property Name="Iteration" Type="Int32" Nullable="false" **DefaultValue="1"** />
并通过向构造函数添加必要的代码:
public Thingy()
{
this.Iteration = 1;
设置MSSQL Server中表和类代码add属性中列的默认值,如下所示:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
对于相同的属性。
this.Active = true;
?我认为DB值在获取时将优先,但是要小心,如果new'ing然后附加一个实体进行更新而不先获取,因为更改跟踪可能会在您希望更新值时看到它。发表评论,因为我已经很长时间没有使用EF了,我觉得这是在黑暗中拍摄。