EF Core更新时修改ViewModel未包含字段的问题排查
问题根因
你的更新逻辑存在两个核心问题,直接导致CreatedBy、DateCreated字段被意外覆盖:
- 使用AutoMapper将视图模型映射为
Location实体时,视图模型中不存在的CreatedBy、DateCreated字段会被自动赋值为类型默认值:可空引用类型CreatedBy默认是null,值类型DateCreated默认是0001/1/1 0:00:00。 - 你对映射生成的、完全没有携带数据库原始值的实体直接调用
_context.Update(location),EF Core会将该实体的所有属性标记为已修改状态,生成的UPDATE语句会覆盖表中所有列的值,自然就把原本保留的创建人、创建时间字段冲掉了;如果这两个字段设置为非空约束,默认值不满足校验规则就会直接抛出异常。
修复方案
推荐写法(最安全)
先查询数据库中已存在的被追踪实体,再将视图模型的允许修改字段映射到该实体上,未映射的创建类字段会自动保留数据库原值,EF Core也只会生成修改变更字段的SQL,不会全列更新:
try { // 先查询数据库原始记录 var location = await _context.Locations .FirstOrDefaultAsync(l => l.LocationId == locationEditVM.LocationId); if (location == null) { return NotFound(); } // 将VM中可编辑字段覆盖到查询到的实体上,CreatedBy、DateCreated不会被改动 mapper.Map(locationEditVM, location); // 实体已被上下文追踪,不需要手动调用Update,保存时自动提交变更 await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!LocationExists(locationEditVM.LocationId)) { return NotFound(); } else { throw; } }
无查询更新写法(适合高性能场景)
如果不想额外做一次查询,可以在附加实体后手动指定不需要更新的字段,将其标记为未修改状态:
try { var location = mapper.Map<Location>(locationEditVM); _context.Attach(location); // 明确指定创建相关字段不参与更新 _context.Entry(location).Property(l => l.CreatedBy).IsModified = false; _context.Entry(location).Property(l => l.DateCreated).IsModified = false; _context.Entry(location).Reference(l => l.LocationCreator).IsModified = false; await _context.SaveChangesAsync(); } // 并发异常处理逻辑保持不变
注意:日常开发中尽量避免直接对未经过数据库查询、手动构造的断开式实体调用
_context.Update()方法,该方法的全字段更新逻辑非常容易导致非预期的数据覆盖。你也可以在AutoMapper的映射配置中全局设置编辑场景下的映射自动忽略CreatedBy、DateCreated字段,从映射层避免字段被误赋值。
内容的提问来源于stack exchange,提问作者oknsyl
相关产品推荐
相关产品推荐

