簡體   English   中英

實體框架的 SQL 列默認值

[英]SQL column default value with Entity Framework

我正在嘗試使用帶有默認 SQL 值的 Code-First EF6。

例如,我有一個“CreatedDate”列/屬性不為空,SQL 中的默認值為“getdate()”

我如何在我的代碼模型中表示它? 目前我有:

<DatabaseGenerated(DatabaseGeneratedOption.Computed)>
Public Property CreatedDate As DateTime

這是否有效,或者即使實際列不應該為空,我是否需要使用可空值,因此 EF 在尚未設置時不會發送值:

<DatabaseGenerated(DatabaseGeneratedOption.Computed)>
Public Property CreatedDate As DateTime?

或者那里有更好的解決方案?

我不希望 EF 處理我的默認值 - 我知道這對我可用,但在我目前的情況下是不可能的。

目前在 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 函數。 這是讓計算工作的關鍵

接受的答案對於 EF6 是正確的,我只添加了EF Core解決方案; (我的解決方案也側重於更改默認值,而不是第一次正確創建它

EF Core仍然沒有 Data-Attribute 。

並且您仍然必須使用 Fluent API; 它確實有一個HasDefaultValue

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Rating)
        .HasDefaultValue(3);
}

請注意,對於 NULL 情況也有HasDefaultValueSql

        .HasDefaultValueSql("NULL");

您也可以使用 Migrations UpDown方法,您可以更改defaultValuedefaultValueSql但您可能需要先刪除索引。 下面是一個例子:

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"
        );


    }
}

[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; }
    }

在遇到同樣的問題后添加此解決方案。 我想使用 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 應該給你你正在尋找的結果。

試試這個。 此代碼默認插入當前日期

//[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime Created { get; set; } = new DateTime();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM