我正在尝试使用默认SQL值的Code-First EF6。
例如,我有一个" CreatedDate"列/属性不为null,SQL的默认值为" getdate()"
如何在我的代码模型中表示这一点?目前我有:
<DatabaseGenerated(DatabaseGeneratedOption.Computed)>
Public Property CreatedDate As DateTime
这是否有效,或者我是否需要使用可空的内容,即使实际列不应为null,因此EF在未设置时也不会发送值:
<DatabaseGenerated(DatabaseGeneratedOption.Computed)>
Public Property CreatedDate As DateTime?
或者那里有更好的解决方案吗?
我不希望EF处理我的默认设置 - 我知道这是我可以使用的,但在我目前的情况下是不可能的。
答案 0 :(得分:18)
目前在EF6中,没有用于定义用于某个属性默认值的数据库函数的属性。您可以对Codeplex进行投票以实现它:
https://entityframework.codeplex.com/workitem/44
实现类似内容的可接受方法是使用Computed
属性与Migrations
一起指定默认数据库函数。
你的课程在C#中看起来像这样:
public class MyEntity
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime Created { get; set; }
}
计算属性不必是可为空的。
然后,您必须运行迁移并手动修改它以包含默认的SQL函数。迁移可能如下所示:
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");
}
}
您会注意到defaultValueSql函数。这是使计算工作的关键
答案 1 :(得分:3)
对于EF6,接受的答案是正确的,我只添加EF Core解决方案; (我的解决方案也侧重于更改默认值,而不是第一次正确创建)
EF Core
中仍然没有数据属性。
您仍然必须使用Fluent API;它确实有一个HasDefaultValue
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Rating)
.HasDefaultValue(3);
}
注意,对于NULL情况也有HasDefaultValueSql
:
.HasDefaultValueSql("NULL");
您还可以使用迁移Up
和Down
方法,您可以更改defaultValue
或defaultValueSql
,但您可能需要先删除索引。这是一个例子:
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"
);
}
}
答案 2 :(得分:0)
尝试一下。默认情况下,此代码会插入当前日期
//[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime Created { get; set; } = new DateTime();
答案 3 :(得分:0)
[mysql]
对于那些不想在每次数据库更新后使用计算并重写它的人,我为数据库部分类编写了扩展方法。当然,有一些东西需要改进或增加,但是现在对于我们的使用,享受来说已经足够了。
请注意,由于对database_schema的访问不是最快的,因此您还需要具有与表名相同的实体名称(或以某种方式重写它)。
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; }
}
答案 4 :(得分:0)
在遇到同样的问题后添加此解决方案。我想使用 SQL Server 中定义的默认值,而不必在 C# 代码中设置默认值。
此行为由 DatabaseGeneratedOption 值控制。 EF 是使用默认值、NULL 值还是指定值因使用的选项而异。以下是为每个选项创建新数据库条目以及是否使用默认值的简化示例。
// Item database declaration
public Item()
{
public string id { get; set; }
public string description { get; set }
}
// Add item to database code. First one uses the default value, the second is
// overriding it using the specified value. Anything inside brackets uses your
// database context and class definition objects.
var itemToAdd1 = new [dbClass].Item
{
id = "CodeTest1"
};
var itemToAdd2 = new new[DBClass].Item
{
id = "CodeTest2",
description = "Default value override in code"
};
[dbContext].Add(itemToAdd1);
[dbContext].Add(itemToAdd2);
[dbContext].SaveChanges();
// The behavior changes based on the DatabaseGeneratedOption setting on the database
// class definition for the description property.
// DatabaseGeneratedOption: None
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string description { get; set; }
Result:
id = "CodeTest1", // First item, using default value
description = NULL // Code sent null value and SQL used it instead of the default
id = "CodeTest2", // Second item, overriding description in code
description = "Default value override in code" // Code override value was used by SQL as expected
// DatabaseGeneratedOption: Identity
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string description { get; set; }
Result:
id = "CodeTest1", // First item, using default value
description = "SQL Default Value" // Code did not send any value and SQL used the DB default value as expected
id = "CodeTest2", // Second item, overriding description in code
description = "Default value override in code" // Code override value was used by SQL as expected
// DatabaseGeneratedOption: Computed
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string description { get; set; }
Result:
id = "CodeTest1", // First item, using default value
description = "SQL Default Value" // Code did not send any value and SQL used the DB default value as expected
id = "CodeTest2", // Second item, overriding description in code
description = "SQL Default Value" // The SQL default value was still used despite setting this property in code.
TLDR: DatabaseGeneratedOption.Identity 应该会给你你正在寻找的结果。