實體框架不包括插入查詢時具有默認值的列

c# entity-framework entity-framework-core

我有一個模型,其中有一些列定義了默認值,如

table.Column<bool>(nullable: false, defaultValueSql: "1")

當我使用context.SaveChanges()在數據庫中保存新實體時,我注意到具有默認值的列未包含在Entity Framework生成的查詢的插入中,因此數據庫中生成的值是默認值而不是我在模型中傳遞的那些。

我是否必須在上下文中設置一些屬性才能通過代碼設置這些屬性?我正在使用EF Core,但我不知道這是否是所有EF版本的一般行為。

更新:代碼非常簡單。這是我所擁有的偽代碼。模型有一些使用約束定義的屬性,例如我在上面描述的table.Column<bool>(nullable: false, defaultValueSql: "1")

我將使用列MyBooleanProperty作為示例。我有服務:

var newModel = new GEAddress();
newModel = someEntity.MyBooleanProperty; //it is false,but ends up as 1 in the database

我正在使用工作單元和存儲庫,所以我有

_unitOfWork.MyModelRepository.Add(newModel);
_unitOfWork.SaveChanges();

在VS的輸出窗口中,我看到它如何在INSERT INTO查詢中發送所有屬性,然後在具有默認值的屬性上執行SELECT 。結果是數據庫中的newModel包含我發送的所有值,但具有默認值的列除外。我無法更改這些表的配置,因為它正被另一個需要這些規則的系統使用。

我想知道解釋為什麼這不僅僅是一個解決方案。我可以解決這個問題,但我想知道為什麼會出現這種情況

一般承認的答案

我會稱這是一個錯誤。

我拼了一個簡單的測試,只是一個帶有一些默認值的類:

public class Test
{
    public int ID { get; set; }
    public int IntDef { get; set; }
    public bool BoolDef { get; set; }
    public DateTime? DateDef { get; set; }
}

(注意DateTime可以為空)

映射:

modelBuilder.Entity<Test>().HasKey(a => a.ID);
modelBuilder.Entity<Test>().Property(s => s.DateDef).HasDefaultValueSql("GETDATE()");
modelBuilder.Entity<Test>().Property(s => s.IntDef).HasDefaultValueSql("1");
modelBuilder.Entity<Test>().Property(s => s.BoolDef).HasDefaultValue(true);
// Equivalent:
// modelBuilder.Entity<Test>().Property(s => s.BoolDef).HasDefaultValueSql("1");

創建表的SQL語句:

CREATE TABLE [Tests] (
    [ID] int NOT NULL IDENTITY,
    [BoolDef] bit NOT NULL DEFAULT 1,
    [DateDef] datetime2 DEFAULT (GETDATE()),
    [IntDef] int NOT NULL DEFAULT (1),
    CONSTRAINT [PK_Tests] PRIMARY KEY ([ID])
);

當我插入一個新的Test而沒有設置任何值時,insert語句是:

INSERT INTO [Tests]
DEFAULT VALUES;
SELECT [ID], [BoolDef], [DateDef], [IntDef]
FROM [Tests]
WHERE @@ROWCOUNT = 1 AND [ID] = scope_identity();

您會看到插入後從數據庫中讀取了三個默認值(以及生成的標識值)。 [順便說一句,這是EF-Core的新功能。在EF6中,在插入(和更新)之後,只從DatabaseGeneratedOption.Computed中讀取標記為DatabaseGeneratedOption.Computed標識值和列值。

這是創建的Test對象:

ID IntDef BoolDef DateDef
1  1      True    21-11-16 19:52:56 

現在我插入一個新的Test並分配所有值,但是,為了好玩,我使用非可空類型的默認值:

var item = new Test
{ 
    IntDef = default(int), // 0 
    BoolDef = default(bool), // false
    DateDef = default(DateTime), // 01-01-01 0:00:00
};

這是SQL語句:

exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [Tests] ([DateDef])
VALUES (@p0);
SELECT [ID], [BoolDef], [IntDef]
FROM [Tests]
WHERE @@ROWCOUNT = 1 AND [ID] = scope_identity();
',N'@p0 datetime2(7)',@p0='0001-01-01 00:00:00'

當然,EF無法推斷默認值是故意分配的。如您所見,僅針對可為空的DateTime列插入指定的值,而不是插入非可空列。現在,插入後不會從數據庫中讀取DateDef的值。

實體值為:

ID IntDef BoolDef DateDef
1  1      True    01-01-01 0:00:00 

保存實體後不是人們所期望的 - 根本不是!

意思是:

在EF-Core中配置具有默認值的屬性時,此默認值與.Net默認值不同, 則無法插入具有.Net類型默認值的實體 (如布爾false )。

我認為這是一個嚴重的錯誤,也許它甚至取消了有關默認值的新EF-Core行為的資格。

加成
正如Ivan的評論中所述,您可以通過添加ValueGeneratedNever()來阻止EF為您設置默認值,例如:

modelBuilder.Entity<Test>().Property(s => s.IntDef)
            .HasDefaultValueSql("1").ValueGeneratedNever();

現在,該值將保存原樣,EF在插入和更新後不會將其讀回。總而言之,我認為定義非可空屬性的默認值是沒有用的。


使用EF Core 1.1.0預覽測試。


熱門答案

如果未將屬性標記為使用適當的屬性計算

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]  

ef將生成帶有屬性值的insert語句。
在更新期間,EF生成UPDATE語句SET子句,僅插入更改的值。

無論如何,您已經有了一個解決方法,如果該屬性僅由DBMS生成,您可以使用上面的屬性,否則您必須在表示實體的類的構造函數中插入默認值。



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow