ASP.NET Core MVC 8中自动维护CreatedAt/UpdatedAt时间字段问题
解决方案:自动维护CreatedAt和UpdatedAt字段
问题根源
- 创建操作:你的
Create方法未手动设置CreatedAt,导致该字段被赋值为DateTime.MinValue(默认值),UpdatedAt保持为null,不符合需求。 - 更新操作:
Edit方法直接实例化新的Gallery对象并调用Update,EF Core会将实体所有字段标记为待更新。新对象的CreatedAt是默认值,会覆盖数据库中已有的原始值,最终导致CreatedAt被重置(甚至变为NULL,如果数据库列允许为空),同时未设置UpdatedAt。
方案一:手动修正Controller代码(快速解决)
1. 修复Create方法
创建Gallery对象时,显式设置CreatedAt为UTC时间:
[HttpPost] [ValidateAntiForgeryToken] public IActionResult Create(GalleryVM galleryVM) { Gallery? galleryExists = _db.Galleries.SingleOrDefault(p => p.Title == galleryVM.Title); if (galleryExists != null) { ModelState.AddModelError("Title", $"{galleryExists.Title} 已存在于系统中。"); } if (ModelState.IsValid) { var galleryObj = new Gallery { Title = galleryVM.Title, IsPrivate = galleryVM.IsPrivate, AccessCode = galleryVM.AccessCode, CreatedAt = DateTime.UtcNow, // 设置创建时间 UpdatedAt = null // 可选,默认即为null }; _db.Galleries.Add(galleryObj); _db.SaveChanges(); TempData["success"] = "Gallery创建成功"; return RedirectToAction("Index"); } return View(galleryVM); }
2. 修复Edit方法
不要直接实例化新对象,先从数据库获取原始实体,仅更新需要修改的字段,并设置UpdatedAt:
[HttpPost] [ValidateAntiForgeryToken] public IActionResult Edit(GalleryVM galleryVM) { if (!ModelState.IsValid) { return View(galleryVM); } var galleryObj = _db.Galleries.Find(galleryVM.Id); if (galleryObj == null) { return NotFound(); } // 仅更新需要修改的字段 galleryObj.Title = galleryVM.Title; galleryObj.IsPrivate = galleryVM.IsPrivate; galleryObj.AccessCode = galleryVM.AccessCode; galleryObj.UpdatedAt = DateTime.UtcNow; // 设置更新时间 _db.SaveChanges(); TempData["success"] = "Gallery更新成功"; return RedirectToAction("Index"); }
方案二:EF Core自动处理(优雅持久化方案)
通过重写DbContext的SaveChanges方法,自动维护时间字段,避免在每个Controller中重复编写代码:
1. 在DbContext中添加时间戳处理逻辑
public class YourDbContext : DbContext { // 构造函数和DbSet定义... public override int SaveChanges(bool acceptAllChangesOnSuccess) { UpdateEntityTimestamps(); return base.SaveChanges(acceptAllChangesOnSuccess); } public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) { UpdateEntityTimestamps(); return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); } private void UpdateEntityTimestamps() { // 跟踪所有新增或修改的Gallery实体 var entries = ChangeTracker.Entries<Gallery>() .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified); foreach (var entry in entries) { if (entry.State == EntityState.Added) { // 创建时设置CreatedAt,UpdatedAt设为null entry.Entity.CreatedAt = DateTime.UtcNow; entry.Entity.UpdatedAt = null; } else if (entry.State == EntityState.Modified) { // 更新时仅设置UpdatedAt,禁止修改CreatedAt entry.Entity.UpdatedAt = DateTime.UtcNow; entry.Property(e => e.CreatedAt).IsModified = false; } } } }
2. 简化Controller代码
此时Create和Edit方法无需手动设置时间字段:
- Create方法只需映射VM到实体并添加,无需处理时间
- Edit方法只需获取原始实体、更新字段后保存,无需处理时间
额外建议
- 确保实体类
Gallery的CreatedAt字段在数据库中配置为不可为空,避免出现NULL值 - 如果希望创建时
UpdatedAt也自动赋值为当前时间,可以移除Added分支中的UpdatedAt = null语句
内容的提问来源于stack exchange,提问作者user18776105
相关产品推荐
相关产品推荐

