.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
相关产品推荐
相关产品推荐

