提交表单时Model无法绑定问题求助
我被这个问题困扰2天了,为简化说明将尽量少写代码。我使用同一个控制器同时处理视图展示与数据保存,我的领域类、视图、控制器如下(问题出在最后一个属性public DateTime CreatedOn,可直接跳至末尾查看完整问题说明):
领域类片段
public partial class Admin_TestCategory { [Key] [Column(Order = 0)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public short TestCategoryId { get; set; } // 其他属性省略... public DateTime CreatedOn { get; set; } }
针对CreatedOn属性的常见问题与解决方案
根据你提到的场景(同一控制器处理展示和保存),大概率是新增时未赋值导致数据库报错或者编辑时覆盖了原有创建时间这类问题,给你整理几个实用的解决办法:
1. 新增数据时自动设置创建时间
如果你的CreatedOn字段在数据库中不允许为NULL,新增时没赋值肯定会触发异常。这里有两种优雅的处理方式:
方式一:在领域类中设置默认值
直接给属性加默认值,实例化时自动赋值:
public DateTime CreatedOn { get; set; } = DateTime.Now;
或者用EF的特性让数据库自动生成(适合SQL Server这类支持默认值的数据库):
[DatabaseGenerated(DatabaseGeneratedOption.Computed)] public DateTime CreatedOn { get; set; }
⚠️ 注意:用Computed的话,得先在数据库表的CreatedOn字段设置默认值(比如GETDATE()),不然EF会报错。
方式二:在控制器的Create方法手动赋值
如果需要更灵活的控制(比如用UTC时间避免时区问题),可以在保存前手动设置:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(Admin_TestCategory model) { if (ModelState.IsValid) { model.CreatedOn = DateTime.UtcNow; db.Admin_TestCategory.Add(model); db.SaveChanges(); return RedirectToAction("Index"); } return View(model); }
2. 编辑数据时保留原有创建时间
编辑时如果视图里没提交CreatedOn的值,模型绑定会把它设成DateTime.MinValue,保存时就会覆盖原来的创建时间。解决办法很简单:
在编辑视图里加个隐藏字段,把原始的CreatedOn值传回去:
@Html.HiddenFor(model => model.CreatedOn)
这样提交表单时,旧的创建时间会被带回控制器,不会被覆盖。
3. 处理模型验证问题
如果你的CreatedOn加了[Required]特性,但视图里没提供这个值,会触发模型验证失败。可以这么处理:
- 要是数据库允许
CreatedOn为NULL,直接把[Required]删掉; - 或者按上面的方法加隐藏字段;
- 也可以在控制器里手动赋值并清除验证错误:
[HttpPost] public ActionResult Edit(Admin_TestCategory model) { if (!ModelState.IsValid) { // 清除CreatedOn的验证错误 ModelState.Remove("CreatedOn"); // 从数据库拿原始的CreatedOn值 var originalCategory = db.Admin_TestCategory.Find(model.TestCategoryId); model.CreatedOn = originalCategory.CreatedOn; // 重新验证模型 if (TryValidateModel(model)) { db.Entry(originalCategory).CurrentValues.SetValues(model); db.SaveChanges(); return RedirectToAction("Index"); } } return View(model); }
4. 进阶:用EF Core阴影属性(如果用的是EF Core)
要是不想在领域类里暴露CreatedOn,可以用EF Core的阴影属性,让数据库自动处理:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Admin_TestCategory>() .Property<DateTime>("CreatedOn") .HasDefaultValueSql("GETDATE()"); }
这样数据库会自动给CreatedOn设默认值,代码里完全不用管。
内容的提问来源于stack exchange,提问作者Wahid Masud

