在需要删除、添加或更新的情况下,AutoMapper是否没有更新嵌套列表的本机方法?
我在带有EF Core的ASP.NET Core应用程序中使用AutoMapper将API资源映射到我的模型。这在我的大部分应用程序中都运行得很好,但我对更新映射嵌套列表的解决方案不满意,在该列表中列出的实例需要持久化。我不想覆盖现有列表,我想删除传入资源中不再存在的实例,添加新实例,并更新现有实例。
型号:
public class MainObject
{
public int Id { get; set; }
public List<SubItem> SubItems { get; set; }
}
public class SubItem
{
public int Id { get; set; }
public int MainObjectId { get; set; }
public MainObject MainObject { get; set; }
public double Value { get; set; }
}
资源:
public class MainObjectResource
{
public int Id { get; set; }
public ICollection<SubItemResource> SubItems { get; set; }
}
public class SubItemResource
{
public int Id { get; set; }
public double Value { get; set; }
}
控制器:
public class MainController : Controller
{
private readonly IMapper mapper;
private readonly IMainRepository mainRepository;
private readonly IUnitOfWork unitOfWork;
public MainController(IMapper mapper, IMainRepository mainRepository, IUnitOfWork unitOfWork)
{
this.mapper = mapper;
this.mainRepository = mainRepository;
this.unitOfWork = unitOfWork;
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdateMain(int id, [FromBody] MainObjectResource mainObjectResource)
{
MainObject mainObject = await mainRepository.GetMain(id);
mapper.Map<MainObjectResource, MainObject>(mainObjectResource, mainObjectResource);
await unitOfWork.CompleteAsync();
return Ok();
}
}
映射配置文件:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<MainObject, MainObjectResource>();
CreateMap<SubItem, SubItemResource>();
CreateMap<MainObject, MainObjectResource>().ReverseMap()
.ForMember(m => m.Id, opt => opt.Ignore())
.ForMember(m => m.SubItems, opt => opt.Ignore())
.AfterMap((mr, m) => {
//To remove
List<MainObject> removedSubItems = m.SubItems.Where(si => !mr.SubItems.Any(sir => si.Id == sir.Id)).ToList();
foreach (SubItem si in removedSubItems)
m.SubItems.Remove(si);
//To add
List<SubItemResource> addedSubItems = mr.SubItems.Where(sir => !m.SubItems.Any(si => si.Id == sir.Id)).ToList();
foreach (SubItemResource sir in addedSubItems)
m.SubItems.Add( new SubItem {
Value = sir.Value,
});
// To update
List<SubItemResource> updatedSubItems = mr.SubItems.Where(sir => m.SubItems.Any(si => si.Id == sir.Id)).ToList();
SubItem subItem = new SubItem();
foreach (SubItemResource sir in updatedSubItems)
{
subItem = m.SubItems.SingleOrDefault(si => si.Id == sir.Id);
subItem.Value = sir.Value;
}
});
}
}
我在这里做的是一个定制映射,但我觉得这是一个非常通用的情况,我希望AutoMapper能够通过某种扩展来处理它。我见过一些映射配置文件使用自定义映射(.AfterMap)的示例,但实际的映射是由AutoMapper的静态实例完成的。我不确定这是否适合我通过依赖项注入使用AutoMapper:我不是一个有经验的程序员,但在我看来这并不合理。
多亏了Ivan Stoev,我开始关注AutoMapper.Collection,这确实是我希望找到的扩展。在实现之后,我的列表会按照我的意愿进行更新。该配置在我的使用中非常简单,因为我只需要指定我的对象的ID。
我的启动配置更改为:
using AutoMapper;
using AutoMapper.EquivalencyExpression;
[....]
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(cfg => {
cfg.AddCollectionMappers();
});
}
[....]
和我的映射配置文件:
CreateMap<SubItem, SubItemResource>().ReverseMap()
.EqualityComparison((sir, si) => sir.Id == si.Id);