.NET6 EF6 Razor Pages(非MVC)表单DateOnly字段未填充入库问题
问题根因
.NET 6 原生模型绑定未提供DateOnly/TimeOnly类型的默认绑定逻辑,表单内type="date"输入框提交的yyyy-MM-dd格式字符串无法被自动转换为DateOnly实例,因此日期字段始终被赋值为类型默认值0001-01-01,该值写入PostgreSQL的date类型字段时会被映射为-infinity。该问题出在模型绑定阶段,和EF Core、PostgreSQL数据库本身无关——你调试到ModelState.IsValid判断行时已经拿不到正确日期值,就可以确认这一点。
解决方案
方案1:全局自定义模型绑定器(一劳永逸,推荐)
- 新增
DateOnly类型专属模型绑定器,适配HTML日期输入框的提交格式:using Microsoft.AspNetCore.Mvc.ModelBinding; using System.Globalization; public class DateOnlyModelBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext)); var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueResult == ValueProviderResult.None) return Task.CompletedTask; bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueResult); var rawValue = valueResult.FirstValue; if (string.IsNullOrWhiteSpace(rawValue)) return Task.CompletedTask; // 优先匹配date输入框默认提交的yyyy-MM-dd格式 if (DateOnly.TryParseExact(rawValue, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsedDate)) { bindingContext.Result = ModelBindingResult.Success(parsedDate); return Task.CompletedTask; } // 兼容其他常见日期格式 if (DateOnly.TryParse(rawValue, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsedDate)) { bindingContext.Result = ModelBindingResult.Success(parsedDate); return Task.CompletedTask; } bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "日期格式无效"); return Task.CompletedTask; } } - 新增绑定器提供器,让框架自动识别
DateOnly类型并使用上述绑定器:using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; public class DateOnlyModelBinderProvider : IModelBinderProvider { public IModelBinder? GetBinder(ModelBinderProviderContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); return context.Metadata.ModelType == typeof(DateOnly) ? new BinderTypeModelBinder(typeof(DateOnlyModelBinder)) : null; } } - 在
Program.cs中全局注册绑定器,插入到提供器列表最前端优先匹配:
注册完成后重启项目,所有builder.Services.AddRazorPages(options => { options.ModelBinderProviders.Insert(0, new DateOnlyModelBinderProvider()); });DateOnly类型的表单字段都会自动完成值绑定,不需要修改现有实体、表单、提交方法代码。
方案2:临时快速修复(无需全局配置)
如果不想新增全局配置,可以直接在Post提交方法中手动从表单取值赋值,绕过默认模型绑定逻辑:
public async Task<IActionResult> OnPost() { // 手动解析绑定两个日期字段 if (DateOnly.TryParseExact(Request.Form["Todo.CreateDate"], "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var createDate)) { Todo.CreateDate = createDate; } if (DateOnly.TryParseExact(Request.Form["Todo.DueDate"], "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dueDate)) { Todo.DueDate = dueDate; } // 清除日期字段的默认绑定错误,避免ModelState校验失败 ModelState.Remove("Todo.CreateDate"); ModelState.Remove("Todo.DueDate"); if (!ModelState.IsValid) { var errors = ModelState.SelectMany(x => x.Value.Errors.Select(z => z.Exception)); return Page(); } await _db.Todo.AddAsync(Todo); await _db.SaveChangesAsync(); TempData["success"] = "ToDo created successfully."; return RedirectToPage("Index"); }
注意事项
- 修复后先在
OnPost方法入口打断点,确认Todo.CreateDate和Todo.DueDate已经拿到表单提交的正确日期值,再执行数据库写入操作 - 该问题仅存在于.NET 6版本,.NET 7及以上版本已经内置
DateOnly/TimeOnly类型的模型绑定支持,不需要额外处理 - 如果日期字段为必填项,记得在实体类属性上添加
[Required]特性,避免空提交时写入类型默认值
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

