将值从DTO映射到业务对象 - 在执行更新时忽略空值

时间:2017-06-02 12:40:04

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

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

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

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

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

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

由于我没有给ProjectDto的所有其他属性赋值,AutoMapper会映射我没有提供值的默认值。

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

{
   "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);
    }

1 个答案:

答案 0 :(得分:0)

从根本上说它应该是这样的,你需要将完整的对象与更改的值放在一起。 PUT期望按照REST标准(api / project)完整对象 - 而不是部分或脏对象。

如果您想要更新特定属性,请使用不同的终点,例如

api/project/prop1    
api/project/prop2

然后将相关值添加到它们。

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

请在auto-mapper中使用null替换功能.. 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);
}