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

.NET Web API执行PUT更新时记录意外被删除的问题排查

问题:PUT请求更新记录时目标记录被误删

在使用Repository模式和DTO实现更新功能时,执行PUT请求后收到成功提示,但目标RCJY实体却被删除。以下是相关日志及代码:

相关日志

The 'RCJY' entity with key '{Id: 1002}' tracked by 'ApplicationDbContext' changed state from 'Unchanged' to 'Modified'.
dbug: 6/4/2023 13:55:26.384 CoreEventId.SaveChangesStarting[10004] (Microsoft.EntityFrameworkCore.Update) 
      SaveChanges starting for 'ApplicationDbContext'.
dbug: 6/4/2023 13:55:26.384 CoreEventId.DetectChangesStarting[10800] (Microsoft.EntityFrameworkCore.ChangeTracking) 
      DetectChanges starting for 'ApplicationDbContext'.
dbug: 6/4/2023 13:55:26.387 CoreEventId.ForeignKeyChangeDetected[10803] (Microsoft.EntityFrameworkCore.ChangeTracking) 
      The foreign key property 'RCJY.RootId' was detected as changed from '65' to '(null)' for entity with key '{Id: 1002}'.
dbug: 6/4/2023 13:55:26.400 CoreEventId.CascadeDeleteOrphan[10003] (Microsoft.EntityFrameworkCore.Update) 
      An entity of type 'RCJY' with key '{Id: 1002}' changed to 'Deleted' state due to severed required relationship to its parent entity of type 'Root'.

控制器代码

[HttpPut("{id}")]
public async Task<IActionResult> PutMOD(int id, UpdateMODDto updateMODDto)
{
    // Check IDs equality
    if (id != updateMODDto.Id)
    {
        return BadRequest($"Not Found11 {id} {updateMODDto.Id}");
    }

    var entity = await _modRepository.GetAsync(id);

    if (entity == null)
    {
        throw new Exception($"MOH ID {id} not found");
    }

    _mapper.Map(updateMODDto, entity);

    try
    {
        await _modRepository.UpdateAsync(entity);
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!await MODExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }
    return NoContent();
}

仓储代码

public async Task UpdateAsync(T entity)
{
    _context.Update(entity);
    await _context.SaveChangesAsync();
}

DTO代码

public class UpdateMODDto : MODDto
{
    public int Id { get; set; }
}

问题原因分析

从EF Core日志可以明确:RCJY实体的RootId外键值从65被修改为null,而该外键是与Root实体的必填关联关系,EF Core的级联删除孤儿规则(Cascade Delete Orphan)被触发,导致RCJY实体被标记为Deleted状态并最终被删除。

解决方案

  • 修正映射配置:确保映射工具(如AutoMapper)不会覆盖实体中原有的RootId有效值。如果更新接口不需要修改该关联,可在映射规则中忽略该字段:
    CreateMap<UpdateMODDto, RCJY>()
        .ForMember(dest => dest.RootId, opt => opt.Ignore());
    
  • 控制DTO字段范围:如果RootId不需要通过更新接口修改,直接从UpdateMODDto(或其基类MODDto)中移除该字段,避免映射时误赋值;
  • 优化仓储更新逻辑:避免使用_context.Update(entity)(会标记所有属性为Modified),改为仅标记需要更新的属性:
    public async Task UpdateAsync(T entity)
    {
        _context.Attach(entity);
        // 示例:仅标记需要更新的属性,比如Name
        _context.Entry(entity).Property(e => e.Name).IsModified = true;
        await _context.SaveChangesAsync();
    }
    
  • 添加输入校验:在DTO中添加数据注解,确保RootId不会被传入null值(若该字段为必填项):
    public class UpdateMODDto : MODDto
    {
        public int Id { get; set; }
        [Required]
        public int RootId { get; set; }
    }
    

内容的提问来源于stack exchange,提问作者Mohammed Abed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 13:44:55