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

Asp.Net Core 3.1 Web Api更新操作误删记录问题排查

问题现象

在 Asp.Net Core 3.1 Web API 中实现部门更新接口时,调用接口后目标部门记录被删除,未执行预期的更新操作。
涉及的数据表结构如下:

Id    Name    Active  CreatedById CreateDate UpdatedById UpdateDate CompanyId RefId
310   HSE       1       54566     12/2/2021                              8

Postman发起请求的操作过程:
Postman操作演示

原接口实现代码:

[HttpPut]
[Route("~/api/entity/UpdateDepartment")]
public async Task<IActionResult> UpdateDepartment([FromBody] TblCompanyDepartmentsXlu department)
{
    try
    {
        Type dept = typeof(TblCompanyDepartmentsXlu);
        int DepartmentId = await companyService.FindDepartment(department.Id, department.RefId);
        TblCompanyDepartmentsXlu Department = await context.Departments.FirstOrDefaultAsync(x => x.Id == DepartmentId);
        foreach(PropertyInfo prop in dept.GetProperties())
        {
            var vally = department.GetType().GetProperty(prop.Name);
            if(vally !=null)
            {
                dept.GetProperty(prop.Name).SetValue(Department, department.GetType().GetProperty(prop.Name).GetValue(department));
            }
        }
        int EmployeeCompanyId = employeeService.GetEmployeeCompanyId(HttpContext);
        Department.UpdateById = EmployeeCompanyId;
        Department.UpdateDate = DateTime.Now;
        await context.SaveChangesAsync();
        return Ok("Department updated successfully!");
    }
    catch (Exception ex)
    {
        return Problem(ex.Message);
    }
}

已尝试的排查手段:

  • 在SaveChangesAsync()前添加/移除context.Departments.Update(Department)语句,问题复现
  • 替换反射赋值写法为手动逐字段赋值,问题仍存在

问题根因
  1. 反射全量赋值破坏EF Core变更追踪
    当前反射逻辑没有做属性过滤,会把传入参数的所有属性值(包括主键、审计字段、导航属性)全量覆盖到已被上下文跟踪的数据库实体上:
    • 如果Postman传入的JSON漏传Id字段,反序列化后Id默认值为0,会直接把原查询出的Id=310的实体主键覆盖为0。EF Core检测到已跟踪实体主键被修改时,会自动将原主键对应的记录标记为Deleted状态,将修改后主键的实体标记为Added状态,执行SaveChangesAsync时就会删除原记录。
    • 如果传入参数包含导航属性(比如关联的员工集合、所属公司对象),反序列化生成的对象中这些属性默认是null或空集合,全量赋值给已跟踪实体后,EF Core会判定原有关联关系全部被移除,如果关系配置了级联删除规则,也会触发主实体被标记为删除。
  2. 查询逻辑冗余易出错
    更新操作本应直接通过前端传入的主键查询目标记录,额外调用FindDepartment通过Id+RefId查询时,因为RefId是本次要补充的新值,原记录中RefId为空,很容易返回错误的DepartmentId(比如0),导致后续操作的目标实体错误。
  3. 非更新字段未做隔离
    反射逻辑没有区分可修改业务字段和固定系统字段,CreatedById/CreateDate这类创建后不应修改的字段、软删除标记字段、并发令牌字段都会被传入值覆盖,极易触发EF Core异常状态。

修复方案
  1. 修正查询逻辑,直接通过主键查询目标实体,移除多余的FindDepartment调用,提前做参数合法性校验
  2. 限制反射赋值范围,只允许更新指定的业务字段,跳过主键、审计字段、导航属性
  3. 移除多余的Update()调用,对EF Core已经跟踪的实体,直接修改属性值即可自动追踪变更,不需要手动调用Update方法

修复后代码参考:

[HttpPut]
[Route("~/api/entity/UpdateDepartment")]
public async Task<IActionResult> UpdateDepartment([FromBody] TblCompanyDepartmentsXlu department)
{
    try
    {
        // 校验传入主键合法性
        if (department.Id <= 0)
        {
            return BadRequest("部门ID参数非法");
        }
        // 直接通过主键查询目标实体,不要用其他字段二次匹配
        TblCompanyDepartmentsXlu dbDepartment = await context.Departments.FirstOrDefaultAsync(x => x.Id == department.Id);
        if (dbDepartment == null)
        {
            return NotFound("指定部门不存在");
        }

        // 显式定义允许前端更新的字段列表,禁止全属性反射
        var allowUpdateProps = new List<string> 
        { 
            nameof(TblCompanyDepartmentsXlu.Name), 
            nameof(TblCompanyDepartmentsXlu.Active), 
            nameof(TblCompanyDepartmentsXlu.RefId), 
            nameof(TblCompanyDepartmentsXlu.CompanyId) 
        };
        
        Type deptType = typeof(TblCompanyDepartmentsXlu);
        foreach (var propName in allowUpdateProps)
        {
            PropertyInfo prop = deptType.GetProperty(propName);
            if (prop == null) continue;
            var newValue = prop.GetValue(department);
            // 可按需新增空值判断逻辑,决定空值是否覆盖原有值
            prop.SetValue(dbDepartment, newValue);
        }

        int employeeCompanyId = employeeService.GetEmployeeCompanyId(HttpContext);
        dbDepartment.UpdateById = employeeCompanyId;
        dbDepartment.UpdateDate = DateTime.Now;
        
        await context.SaveChangesAsync();
        return Ok("Department updated successfully!");
    }
    catch (Exception ex)
    {
        return Problem(ex.Message);
    }
}

另外需要确认Postman请求Body中正确传入了目标部门的Id值(即310),不要漏传或错传。


内容的提问来源于stack exchange,提问作者Rani Radcliff

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.15 16:16:00