.NET Core 7中移除JSON空值并转换为实体的解决方案
解决.NET Core 7更新时未提交字段被设为null覆盖数据库值的问题
问题核心
前端提交的JSON中未包含Description等字段时,对应的DTO属性会被初始化为null,映射到实体后执行更新操作会覆盖数据库中该字段的原有值。
可行解决方案
方案1:使用HttpPatch + JsonPatchDocument(推荐REST标准方式)
这是REST规范中处理部分更新的标准方案,仅对指定字段进行修改。
控制器代码
[HttpPatch("{id}")] public async Task<IActionResult> Patch(int id, [FromBody] JsonPatchDocument<CompanyUpdateDto> patchDoc) { var existingCompany = await _repository.GetByIdAsync(id); if (existingCompany == null) return NotFound(); // 将数据库现有实体转换为DTO var companyDto = _mapper.Map<CompanyUpdateDto>(existingCompany); // 应用Patch操作到DTO patchDoc.ApplyTo(companyDto); // 将DTO的变更映射回原有实体 _mapper.Map(companyDto, existingCompany); // 自动更新修改时间 existingCompany.UpdatedDate = DateTime.UtcNow; await _unitOfWork.CommitAsync(); return NoContent(); }
前端提交的Patch请求示例
[ { "op": "replace", "path": "/name", "value": "Test1" } ]
方案2:手动对比更新(适合简单实体场景)
直接从数据库获取原有实体,仅更新DTO中非null的字段。
服务层代码
public async Task UpdateAsync(CompanyUpdateDto updateDto) { var existingCompany = await _repository.GetByIdAsync(updateDto.Id); if (existingCompany == null) throw new KeyNotFoundException("目标公司不存在"); // 仅更新非null的字段 if (!string.IsNullOrEmpty(updateDto.Name)) existingCompany.Name = updateDto.Name; if (!string.IsNullOrEmpty(updateDto.Description)) existingCompany.Description = updateDto.Description; // 若DTO未传UpdatedDate,则自动设置为当前时间 existingCompany.UpdatedDate = updateDto.UpdatedDate ?? DateTime.UtcNow; _repository.Update(existingCompany); await _unitOfWork.CommitAsync(); }
控制器代码调整
[HttpPut] public async Task<IActionResult> Update([FromBody] CompanyUpdateDto updateCompany) { await _service.UpdateAsync(updateCompany); return CreateActionResult(CustomResponseDto<CompanyUpdateDto>.Success(204)); }
方案3:配置AutoMapper仅映射非null属性
通过AutoMapper的条件配置,只将DTO中非null的属性映射到实体。
AutoMapper配置
public class CompanyProfile : Profile { public CompanyProfile() { CreateMap<CompanyUpdateDto, Company>() // 对所有成员设置条件:仅当源属性非null时才映射 .ForAllMembers(opt => opt.Condition((src, dest, srcMember) => srcMember != null)); } }
控制器代码调整
[HttpPut] public async Task<IActionResult> Update([FromBody] CompanyUpdateDto updateCompany) { var existingCompany = await _repository.GetByIdAsync(updateCompany.Id); if (existingCompany == null) return NotFound(); // 仅将DTO中非null的属性映射到原有实体 _mapper.Map(updateCompany, existingCompany); existingCompany.UpdatedDate = DateTime.UtcNow; await _service.UpdateAsync(existingCompany); return CreateActionResult(CustomResponseDto<CompanyUpdateDto>.Success(204)); }
服务层代码保持不变
public async Task UpdateAsync(Company entity) { _repository.Update(entity); await _unitOfWork.CommitAsync(); }
为什么你之前的尝试无效
你尝试通过序列化忽略null再转换实体的方式不可行,原因是:DTO中未提交的字段已经被初始化为null,序列化再反序列化后依然是null,且Convert.ChangeType无法直接将JSON字符串转换为实体类型(正确做法是用JsonSerializer.Deserialize,但这也解决不了null值的来源问题)。核心问题是无法区分“前端未传字段”和“前端主动传了null”,上述方案从根源上规避了这个问题。
内容的提问来源于stack exchange,提问作者fatihgun
相关产品推荐
相关产品推荐

