EF6中一個關於時間類型 datetime2 的坑


在一個訪問下位機的程序中,返回的時間戳有時候因斷線產生0001年01月01日的時間,而原先使用拼接SQL進行數據存儲的操作時,這個問題是可以跳過的。

這次把拼接SQL的部分重新改為EF進行管理,這個坑就不能避免了。

 

datetime2是個什么鬼?

datetime2 是一個在范圍和精度上都優於 datetime 類型的時間類型。

傳統的datetime類型,時間的最小值是 1753-01-01,精度最多到秒后三位。這也是被大家所熟知的時間類型。在今天的多數業務下,這個時間在使用上沒有什么問題。

但是總有些不那么和諧的玩藝,居然總想搞個大新聞,用時間類型來標志異常(其實也沒什么問題,畢竟異常了未必有時間戳,那么空的時間戳就出現了)

於是,時間的Null怎么表示呢?多數的策略選擇了 0001-01-01 00:00:00這樣一個時間。畢竟比起大家都知道的 1753-01-01,這個公元元年第一天的時間更具有標志意義。

但是傳統那個Datetime它記不了這個值啊。

 

所以,一個更長、更細(好像哪里不對的樣子)的datetime2類型出現了。 這個類型其實也基本被各個語言都實現了,所以算是一個標准。只是大多數程序員在很長的職業生涯里未必真的遇到這種情況,所以並不被多數人熟知。

那么,報錯的實際情況是什么呢?

System.Data.SqlClient.SqlException: 從 datetime2 數據類型到 datetime 數據類型的轉換產生一個超出范圍的值.

這個其實有點誤導性質。

這個轉換實質上是EF內部完成的。在EF創建了DB First的Model后,正常情況下,相關的這個映射字段的類型依然和數據庫設計保持一致,是datetime。 但是,在賦值的過程中,如果這個字段沒有賦值、或者賦值給出了0001-01-01的時間(或者小於1753的時間),結果就是EF會根據表的設計約束(顯然就是字段必填了)轉換成一個實質上的datetime2類型。(確實這個類型沒辦法自己聲明出來啊,所以這應該算一個坑)

然后,接下來的故事就必然發生了,在EF執行 db.SaveChanges()操作時,數據庫只接受datetime類型的日期,而不接受datetime2類型的值。進而就拋出這樣一個錯誤。

 

so?解決方案呢?

顯然,最直接的解決策略是改數據表的字段為 datetime2類型。 這個是支持的,所以沒有什么大問題。當然后果就是,如果進行一個按時間正序進行的查詢,那么肯定會有大量的0001-01-01的數據出現。在多數業務系統中,這種數據應該直接判定為錯數而丟棄。

稍好的方案是對代碼中的datetime設置為 Nullable 類型,允許空。 相比之下,這個做法更優雅一些,程序的邏輯更直觀。

當然,也有一些算是奇淫技巧的方案,酌情吧:

  1. 在C#中用new DateTime(year,month,day,hour,minute,second)來限制精度
  2. 這個方法不太推薦,將model的edmx中的providerManifestToken設置成2005,這樣ef就默認轉化成datetime

個人覺得,這倆方案並不和諧。其一,需要在時間類型上作不少限制,其二,這樣修改配置的思路基本上是只解決當前問題,不顧其他影響的思路。

在我的實際案例里,因為這個值需要作為標記異常的用途,所以在不修改前一個版本的底層的要求下,暫時給設置成1753-01-01確保兼容,在全新部署的版本上再進一步利用這個標志。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM