解决datetime2转datetime越界错误:视图中如何实现日期输入?
嘿,我来帮你搞定这个问题!咱们先解决那个烦人的SqlException,再一步步实现视图里的日期输入功能~
这个错误的核心原因是C# DateTime类型和SQL Server datetime类型的范围不匹配:
- C#里的
DateTime默认值是0001-01-01,但SQL Server的datetime类型最小只能存1753-01-01 - 如果你没给
Time_Dep或Time_Arrive赋值就提交表单,EF会把默认的0001-01-01传到数据库,直接超出范围触发错误;另外用户输入早于1753-01-01的日期也会触发这个问题。
给你两个靠谱的修复方案:
方案1:把数据库字段改成datetime2(推荐)
datetime2是SQL Server更现代的日期类型,范围和C#的DateTime完全匹配(从0001-01-01到9999-12-31),彻底避免范围问题。
如果用Code First开发,直接在模型字段上加[Column(TypeName = "datetime2")]注解就行:
public class Plane_Schedule { [Key] public int Plane_id { get; set; } public int PlaneSch_id { get; set; } public string Plane_LocTo { get; set; } public string Plane_LocFrom { get; set; } [Column(TypeName = "datetime2")] public DateTime Time_Dep { get; set; } [Column(TypeName = "datetime2")] public DateTime Time_Arrive { get; set; } public virtual List<Ticket> Tickets { get; set; } }
更新数据库迁移后,字段类型就改成datetime2了,再也不会有范围溢出的问题。
方案2:限制输入日期范围(适合不能改数据库的场景)
如果没法修改数据库,那就要确保用户输入的日期不早于1753-01-01,同时给字段设置合理的默认值:
- 在模型构造函数里设置默认值为SQL datetime的最小值:
public Plane_Schedule() { Time_Dep = new DateTime(1753, 1, 1); Time_Arrive = new DateTime(1753, 1, 1); }
- 给模型加验证注解,提前拦截非法日期:
[Display(Name = "Departure Time")] [DataType(DataType.DateTime)] [Range(typeof(DateTime), "1753-01-01", "9999-12-31", ErrorMessage = "Departure date must be on or after 1753-01-01")] public DateTime Time_Dep { get; set; } [Display(Name = "Arrival Time")] [DataType(DataType.DateTime)] [Range(typeof(DateTime), "1753-01-01", "9999-12-31", ErrorMessage = "Arrival date must be on or after 1753-01-01")] public DateTime Time_Arrive { get; set; }
接下来咱们在视图里做一个友好的日期时间输入控件,让用户不用手动敲格式,还能避免输入错误:
步骤1:完善模型的注解(增强视图体验)
除了之前的注解,加上DisplayFormat让视图显示和编辑时的格式更规范:
[Display(Name = "Departure Time")] [DataType(DataType.DateTime)] [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd HH:mm}", ApplyFormatInEditMode = true)] [Column(TypeName = "datetime2")] // 用方案1的话加这个 [Range(typeof(DateTime), "1753-01-01", "9999-12-31", ErrorMessage = "Departure date must be on or after 1753-01-01")] // 用方案2的话加这个 public DateTime Time_Dep { get; set; } [Display(Name = "Arrival Time")] [DataType(DataType.DateTime)] [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd HH:mm}", ApplyFormatInEditMode = true)] [Column(TypeName = "datetime2")] [Range(typeof(DateTime), "1753-01-01", "9999-12-31", ErrorMessage = "Arrival date must be on or after 1753-01-01")] public DateTime Time_Arrive { get; set; }
步骤2:在视图中生成日期时间选择控件
在你的Create/Edit视图里,用Html.EditorFor配合HTML5的datetime-local类型,浏览器会自动生成带日历和时间选择器的控件:
<div class="form-group"> @Html.LabelFor(model => model.Time_Dep, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Time_Dep, new { htmlAttributes = new { @class = "form-control", type = "datetime-local", min = "1753-01-01T00:00" // 用方案2的话加这个,限制最小日期 } }) @Html.ValidationMessageFor(model => model.Time_Dep, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Time_Arrive, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Time_Arrive, new { htmlAttributes = new { @class = "form-control", type = "datetime-local", min = "1753-01-01T00:00" } }) @Html.ValidationMessageFor(model => model.Time_Arrive, "", new { @class = "text-danger" }) </div> </div>
这样用户点击输入框就能弹出可视化的日期时间选择器,不用手动输入字符串,大大降低错误率。
步骤3:控制器里加强验证(双重保障)
最后在控制器的Post方法里再做一次验证,确保万无一失:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Create([Bind(Include = "Plane_id,PlaneSch_id,Plane_LocTo,Plane_LocFrom,Time_Dep,Time_Arrive")] Plane_Schedule plane_Schedule) { // 手动验证日期范围(如果没加模型注解的话) if (plane_Schedule.Time_Dep < new DateTime(1753, 1, 1)) { ModelState.AddModelError("Time_Dep", "Departure date must be on or after January 1, 1753."); } if (plane_Schedule.Time_Arrive < new DateTime(1753, 1, 1)) { ModelState.AddModelError("Time_Arrive", "Arrival date must be on or after January 1, 1753."); } if (ModelState.IsValid) { db.Plane_Schedules.Add(plane_Schedule); db.SaveChanges(); return RedirectToAction("Index"); } return View(plane_Schedule); }
这样一套操作下来,不仅能解决那个烦人的SqlException,还能给用户提供友好的日期输入体验,再也不会因为日期问题触发错误啦!
内容的提问来源于stack exchange,提问作者Stack Developer

