使用 AddTypeMap 調整型別映射,讓 Dapper 乖乖寫入 0001-01-01 到 datetime2
這週忙著打黑悟空,簡單記一下前陣子同事遇到的一個場景:
由於曆法的關係,SQL Server 的 datetime 範圍只能從 1753-01-01 開始。
如果我們要寫入 0001-01-01 進去的話就會報錯
SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.
補充:關於曆法的部份,有興趣的朋友可以參考這篇:
淺談:消失的日期,以關聯式資料庫的日期資料類型為例 - 德瑞克:SQL Server 學習筆記
這時候通常去把資料表欄位改成香香的 datetime2 就可以搞定,爽爽寫入 0001-01-01。
但如果我們正在使用 Dapper,預設會把 C# 的 DateTime 映射到 SQL Server 的 datetime,所以還是會寫入失敗。
這時候我們就需要明確地請 Dapper 幫我們使用 datetime2 來進行處理。
如果傳遞的只是某個參數,使用 DbType
就行了:
var parameters = new DynamicParameters();
parameters.Add("@Birthday", birthday, DbType.DateTime2);
但如果傳遞的是某個 Model 裡的特定欄位呢?例如:
public class HistoricalEvent
{
public Guid EventId { get; set; } // 歷史事件的唯一識別碼
public string Description { get; set; } // 歷史事件的描述
public DateTime EventDate { get; set; } // 歷史事件發生的日期, 可能早於 1753 年!
}
這種時候就可以使用 AddTypeMap 來調整一下 Dapper 的映射型別。
首先確認 DB 的欄位已經修改為 datetime2,接著我們就可以使用 SqlMapper.AddTypeMap
來幫 Dapper 加上一些小小的型別轉換:
// 粗暴地添加 TypeMap,讓 Dapper 知道 DateTime 現在對應到 DB 的 datetime2 了
// 等等 Dapper 才會乖乖地把 0001/1/1 寫進去= =
SqlMapper.AddTypeMap(typeof(DateTime), System.Data.DbType.DateTime2);
// 示範用資料
var historicalEvent = new HistoricalEvent
{
EventId = Guid.NewGuid(),
Description = "公曆紀元開始",
EventDate = DateTime.Parse("0001-01-01")
};
var sql =
@"
insert into HistoricalEvents (
EventId,
Description,
EventDate)
values(
@EventId,
@Description,
@EventDate);
";
using (var conn = this.Connection)
{
var result = await conn.ExecuteAsync(sql, historicalEvent);
result.Dump();
}
備註:因為我們改的是靜態方法裡的全域設定,所以確定要調整型別映射的話,可以把
AddTypeMap
移動到註冊的地方,例如 Program。方便後續維護的時候好管理,也避免其他同事不知道你偷改,減少被扁的醫療費用。
此外,雖然微軟把拔也建議改用 dateTime2,但如果你會想先了解這兩者的不同(例如精度)再進行處理,可以參考以下的文章:
大多數時候,我們不太需要寫入 0001-01-01,甚至 1753 年之前的日期
(像是日期尚未決定之類的場合,用 Null 表示會貼切)
但夜路走多了,總會遇到 0001-01-01。像是處理古老的遺留代碼、業務邏輯必須區分「還沒設定」和「沒有資料」的狀況,又或是像上面的例子,西元的開始真的就在那一天,這種時候就沒辦法了。
因此順手筆記下來,以後再回來抄,阿彌陀佛。
延伸閱讀
- SQL DateTime 型別陷阱 - 黑暗執行緒:另一個將 C# dateTime 映射到 SQL datetime2 的例子,用來解決 SQL datetime 在毫秒的精度問題
- Dapper.NET Tutorial => Custom Mapping:需要自製 Mapping 的時候參考
參考資料
其他文章
哈囉,如果你也有 LikeCoin,也覺得我的文章有幫上忙的話,還請不吝給我拍拍手呦,謝謝~ ;)