You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core更新时修改ViewModel未包含字段的问题排查

问题根因

你的更新逻辑存在两个核心问题,直接导致CreatedBy、DateCreated字段被意外覆盖:

  1. 使用AutoMapper将视图模型映射为Location实体时,视图模型中不存在的CreatedBy、DateCreated字段会被自动赋值为类型默认值:可空引用类型CreatedBy默认是null,值类型DateCreated默认是0001/1/1 0:00:00。
  2. 你对映射生成的、完全没有携带数据库原始值的实体直接调用_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.18 16:15:42