如何确保C#的DateTime类型属性不传入datetime2值至SQL Server?
这个问题我之前在处理表值参数的时候也踩过坑,本质是SQL Server的datetime和datetime2类型的范围不匹配导致的:SQL的datetime只支持1753-01-01 00:00:00到9999-12-31 23:59:59.997,而C#的DateTime和SQL的datetime2范围一致(从0001-01-01开始),所以当你的CreatedDate包含早于1753的值时,就会触发这个转换错误。下面是几个可行的处理方案:
1. 提前过滤/修正超出范围的DateTime值
在把数据填充到DataTable之前,先检查每个CreatedDate是否在SQL datetime的合法范围内,不符合的话根据业务逻辑调整:
// SQL datetime的最小有效值 var minSqlDatetime = new DateTime(1753, 1, 1, 0, 0, 0); // SQL datetime的最大有效值(包含精度限制) var maxSqlDatetime = new DateTime(9999, 12, 31, 23, 59, 59, 997); foreach (var group in groupList) { if (group.CreatedDate < minSqlDatetime) { // 方案1:设为SQL datetime的最小值 group.CreatedDate = minSqlDatetime; // 方案2:如果业务允许空值,改成可空类型后设为null // group.CreatedDate = null; } else if (group.CreatedDate > maxSqlDatetime) { group.CreatedDate = maxSqlDatetime; } }
2. 修改SQL表值参数的类型(最彻底的方案)
如果业务允许,直接把SQL Server中表值参数@ADGroupPreInsert对应的表类型里的CreatedDate列改成datetime2类型。这样C#的DateTime可以直接传入,不需要任何转换,从根源上避免类型不匹配问题。
3. 填充DataTable时用SqlDateTime显式转换
SqlDateTime是专门对应SQL datetime的类型,它会自动校验范围,不过要注意如果值超出范围会抛出OverflowException,所以最好提前做检查:
foreach (var group in groupList) { try { var sqlSafeDate = new SqlDateTime(group.CreatedDate); dataTable.Rows.Add( group.Id, group.Name, sqlSafeDate.Value ); } catch (OverflowException) { // 处理超出范围的情况,比如设为默认值或者跳过该行 dataTable.Rows.Add(group.Id, group.Name, minSqlDatetime); } }
4. 改用可空DateTime类型(DateTime?)
如果业务允许CreatedDate为空,可以把GroupModel里的CreatedDate定义改成DateTime?(可空类型),这样当源值超出SQL datetime范围时,可以直接设为null,避免强制转换导致的数据失真,前提是SQL对应的列允许空值。
为什么会出现这个错误?
C#的DateTime类型范围和SQL的datetime2完全一致,所以能正常接收源的datetime2值,但当你把这些值传入定义为datetime的SQL参数/列时,SQL Server会尝试把datetime2值转成datetime,一旦值不在datetime的范围内,就会抛出“值超出范围”的错误——表值参数会校验所有行的数据,所以只要有一行不符合就会触发整个插入失败。
内容的提问来源于stack exchange,提问作者user9393635

