今天在用EF做插入操作的時候發現數據庫中一個datetime類型的字段(CreateDate)的值居然全部為null。於是趕緊看表結構發現CreateDate字段居然是允許為空的。
雖然為空,但是設置了默認值getdate(),按說不應該為null的。於是開始測試。
字段允許Null值的情況
Users表結構如下:

假如一個字段有了默認值,並且又允許為Null,在做插入操作時會發生什么?
如上圖中的表結構,CreateDate是允許為null的,而又有默認值getdate()。這樣在用傳統SQL語句和EF做插入操作時分別會發生什么呢?
先看傳統SQL語句:
insert into Users(Username,Password) values('test','123456')
插入結果:

使用SQL完全沒有問題。
再看使用EF:
Users user = new Users(); user.Username = "test2"; user.Password = "123456"; TestEntities entity = new TestEntities(); entity.Users.Add(user); int result = entity.SaveChanges();
插入結果:

使用EF插入居然為NULL!!!於是立刻打開SQL Server Profiler監測生成的SQL,EF居然生成了下面的SQL:

沒賦值的字段,EF生成SQL的時候居然都置為NULL,管你有沒有設默認值!
可能是EF比較嬌情,我字段允許為NULL,EF生成SQL時就置為null了。那我把字段設為不允許為NULL,EF又會生成怎樣的SQL呢?
字段不允許Null值的情況
Users表結構如下:

這一次CreateDate不允許為NULL了,使用SQL理所當然地一切正常:
insert into Users(Username,Password) values('admin','123456')

那再看下用EF:
Users user = new Users(); user.Username = "admin"; user.Password = "123456"; TestEntities entity = new TestEntities(); entity.Users.Add(user); int result = entity.SaveChanges();
結果。。。

”從 datetime2 數據類型到 datetime 數據類型的轉換產生一個超出范圍的值。“
EF很傲嬌地拋了個異常!!為什么會拋出這個異常?這個異常神馬意思?於是照妖鏡SQL Server Profiler又出場了:

日期類型的字段如果不賦值會給默認值“0001-01-01 00:00:00”,這和生成SQL無關,是在生成SQL之前CLR進行的初始化操作,同理String類型的字段如果不賦值會給默認值String.Empty!
但是,那也不至於拋異常啊,頂多字段值存儲為“0001-01-01 00:00:00”唄。
於是查MSDN發現datetime類型的日期范圍僅支持1750-01-01 00:00:00至9999-12-31 23:59:59.997,而0001-01-01 00:00:00不在此范圍內,所以拋出異常。原來如此。
datetime和datetime2支持的日期范圍如下:

結論
如此看來無論字段有沒有默認值,使用EF插入時都沒有任何實際作用了。因此如果你使用EF建議干脆直接寫死算了。比如user.CreateDate = DateTime.Now。
ps.這里只是舉個例子,事實上,在實際項目中不建議寫成DateTime.Now,因為數據庫系統時間和服務器時間一般是不同的,筆者實際項目中其實是封裝了一個方法叫GetSQLServerSystemDateTime。
除此之外是否還有別的方法呢?
方法是有的,打開EF的.edmx文件,找到<EntityType Name="表名">節點,把如下節點:
<Property Name="CreateDate" Type="datetime" Nullable="false" />
改成:
<Property Name="CreateDate" Type="datetime" Nullable="false" StoreGeneratedPattern="Computed" />
即可。
StoreGeneratedPattern是一個枚舉:
- None 一個值,指示它不是服務器生成的屬性。這是默認值。如果沒有StoreGeneratedPattern屬性,其值就默認為None。
- Identity 執行插入時生成一個值,但在執行更新時保持不變。
- Computed 執行插入和更新時都將生成一個值。
筆者經過實際測試發現:
- 如果將StoreGeneratedPattern值設置為Identity,只要一修改CreateDate字段就會拋異常;
- 如果把StoreGeneratedPattern值設置為Computed不會拋異常,但值仍然沒有被修改,即使你寫了user.CreateDate = "xxx"。
所以如果你使用EF,我還是建議采用第一種方式,直接在程序中賦值user.CreateDate = xx.GetSQLServerSystemDateTime()。這種方法省事且安全。修改edmx文件太麻煩,況且每增加一個datetime類型的字段都要修改一次edmx文件。
最后感謝大家提出的建議,歡迎大家親自測試並把結果反饋給我。
