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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 04:20:14