2016-11-15 133 views
4

我已經有使用默認值定義如下實體框架不包括在插入與缺省值的列到查詢

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

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

我是否必須在上下文中設置一些屬性才能夠通過代碼設置這些屬性?我使用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具有我發送的所有值,但具有默認值的列除外。 我無法更改這些表的配置,因爲其他系統需要使用這些規則。

我想知道爲什麼這不僅僅是一個解決方案的解釋。我可以解決這個問題,但我想知道爲什麼這種行爲是發生

+0

一般行爲是「列的默認值是插入新行但未爲該列指定值時將插入的值」。 在你的情況看起來你正在失去設置的值。你可以發佈你的代碼嗎? – kumaro

+0

FWIW,EF6不支持默認值 - 即使非'NULL'列具有明確的默認值集,您也必須明確指定所有列的值。 – Dai

+0

在這種情況下,您可以刪除列的配置以禁用默認值行爲,這樣默認值將由數據庫提供。你能顯示你的代碼(DbContext,Mappings,Pocos)嗎? –

回答

3

我會稱之爲錯誤。

我扣合在一起,一個簡單的測試,只是一些默認類:

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

(請注意,日期時間爲空)

映射:

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 INTO [Tests] 
DEFAULT VALUES; 
SELECT [ID], [BoolDef], [DateDef], [IntDef] 
FROM [Tests] 
WHERE @@ROWCOUNT = 1 AND [ID] = scope_identity(); 

您將看到三個默認值(和生成的標識值)從後插入數據庫中讀取。 [順便說一下,這在EF-Core中是新的。在EF6中,插入(和更新)後僅從數據庫中讀取標記爲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 

沒有什麼人會拯救實體--not在所有後想到!

這意味着:

在配置屬性與EF-核心默認值,這個默認值是比淨默認值不同,你不能爲默認值插入一個實體.Net類型(如布爾型的false)。

我認爲這是一個嚴重的錯誤,可能它甚至會使有關默認值的EF-Core新行爲失去資格。

加成
正如伊萬的評論說,你可以從你設置默認值的加入ValueGeneratedNever()停止EF,例如:

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

現在的價值將被保存,因爲它是和EF在插入和更新後將不會讀回它。總而言之,我認爲定義不可爲空的屬性的默認值是沒有用的。


經過EF Core 1.1.0預覽測試。

+0

我會將此標記爲答案,因爲它確認該行爲確實存在問題。謝謝! – monica

+0

只需添加一下即可,無法在運行時動態控制該行爲。你所能做的就是使用'ValueGeneratedNever()'靜態關閉它。 –

+0

謝謝@Ivan,還不知道這個。我認爲我們可以準備好針對這種行爲提出的大量問題。 –

1

如果你不標記屬性作爲與適當的屬性值計算

[DatabaseGenerated(DatabaseGeneratedOption.Computed)] 

EF將生成插入語句屬性值。
在更新期間,EF生成UPDATE語句的SET子句只插入更改的值。

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

+0

'DatabaseGeneratedOption.Computed'指示其值始終由數據庫提供的列,導致EF忽略來自用戶代碼的該列上的更新。 OP僅希望提供默認值,以便在未指定值時使用,但能夠在插入或修改時指定它。 – CodeCaster

+0

我同意@bubi,我有與該屬性的其他列,他們工作得很好。我的問題是默認值沒有得到插入的價值 – monica

+0

@CodeCaster問題是EF的概念*未指定*不存在(存在被跟蹤實體的_changed_概念),所以如果你沒有指定'計算'EF總是會生成插入語句。 – bubi