【问题标题】:Mapping values from DTO to business object - ignore null values when doing updates将值从 DTO 映射到业务对象 - 进行更新时忽略空值
【发布时间】:2017-06-02 12:40:03
【问题描述】:

我正在使用 AutoMapper 在我的 DTO 和业务对象之间进行映射。这很好用。

但是,我有一个 Web API,它接受 PUT 请求以更新实体。动作如下所示:

public virtual async Task<IHttpActionResult> Put(string id, [FromBody] ProjectDto projectDto)

当从 ProjectDto 映射到 Project(业务对象)时,它会映射 ProjectDto 中的所有值。假设我想通过将以下 JSON 发送到 API 来更新项目名称:

{
    "projectId": 10,
    "name": "The new name"
}

由于我没有为 ProjectDto 的所有其他属性指定值,因此 AutoMapper 将在我没有提供值的地方映射默认值。

例如,如果 ProjectDto 有几个额外的字段,它们现在将为空:

{
   "projectId": 10,
   "name": "The new name",
   "createdDate": null,
   "manager": null
} 

..等等

我的映射如下所示:

cfg.CreateMap<ProjecDto, Project>()
                .ForAllMembers(opt => opt.Condition(x => x != null));

.. 但 AutoMapper 仍在映射空值。

我查看了this question,但是当我尝试复制他们的解决方案时,我只是得到“ProjectDto 不包含 IsSourceValueNull 的定义”。

Web API 控制器操作(它是通用的,TDto 是一个空标记接口。T 和 TDto 在运行时分别解析为 Project 和 ProjectDto。):

 public virtual async Task<IHttpActionResult> Put(string id, [FromBody] TDto model)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        var entity = _dataContext.GetRepository<T>().GetById(id);

        if (entity == null)
            return NotFound();

        var entityToUpdate = _mapper.Map<T>(model);
        var updatedEntity = _dataContext.GetRepository<T>().Update(entity);
        var updatedEntityDto = _mapper.Map<TDto>(updatedEntity);

        return Ok(updatedEntityDto);
    }

【问题讨论】:

  • 您想合并到一个已初始化的对象上并保留未指定的目标值或以某种方式从 api 请求 json 中可选的目标值?小心,你怎么知道 null 意味着清除值或不发送值的意图?
  • 不确定我是否理解您的意思,但我只想更新来自客户端的 ProjectDto 中指定的字段
  • 嗯。真的吗?正如您在更新的问题中看到的那样,我有一个通用控制器,并且并不真正知道编译时对象上存在哪些属性。

标签: c# asp.net-web-api automapper


【解决方案1】:

基本上应该是这样,您需要放置具有更改值的完整对象。 PUT 期望按照 REST 标准(api/project)的完整对象 - 而不是部分或脏对象。

如果您想更新特定属性,请使用不同的端点。

api/project/prop1    
api/project/prop2

然后给它们赋予相关的值。

如果您想覆盖此行为,请编写您自己的映射来放置这些条件。

请在自动映射器中使用空替换功能.. https://github.com/AutoMapper/AutoMapper/wiki/Null-substitution

选项:2(可能是 Hack)

cfg.CreateMap<ProjecDto, Project>();

cfg.CreateMap<Project, Project>()
            .ForAllMembers(opt => opt.Condition(x => x != null));

和 API

public virtual async Task<IHttpActionResult> Put(string id, [FromBody] TDto model)
{
    if (!ModelState.IsValid)
        return BadRequest(ModelState);

    var entity = _dataContext.GetRepository<T>().GetById(id);

    if (entity == null)
        return NotFound();

    var entityToUpdate = _mapper.Map<T>(model);
    entityToUpdate = _mapper.Map<T, T>(entityToUpdate, entity); // overwriting the db object with new vals, may be swap around it, I haven't tested.    
    var updatedEntity = _dataContext.GetRepository<T>().Update(entityToUpdate);
    var updatedEntityDto = _mapper.Map<TDto>(updatedEntity);

    return Ok(updatedEntityDto);
}

【讨论】:

  • 我使用进行更新的 Web API 操作方法更新了问题。
  • 我没有尝试创建新对象,也没有使用 POST。我正在使用 PUT。
  • POST / PUT 需要按照 REST 规范的完整对象。然而,这不是世界末日。
  • 你是对的。我应该接受我想要完成的 PATCH 请求。不过,我的问题仍然是一样的:/
  • 正如我所说,使用采用现有目的地的重载: Mapper.Map(source, destination);看答案
猜你喜欢
  • 2020-06-11
  • 2015-07-29
  • 2020-11-25
  • 2023-01-26
  • 1970-01-01
  • 2021-05-16
  • 1970-01-01
  • 2010-11-23
  • 1970-01-01
相关资源
最近更新 更多