使用AutoMapper添加、更新和删除列表中的项目新和、项目、列表中、AutoMapper

2023-09-04 01:26:12 作者:_漠然╮

在需要删除、添加或更新的情况下,AutoMapper是否没有更新嵌套列表的本机方法?

我在带有EF Core的ASP.NET Core应用程序中使用AutoMapper将API资源映射到我的模型。这在我的大部分应用程序中都运行得很好,但我对更新映射嵌套列表的解决方案不满意,在该列表中列出的实例需要持久化。我不想覆盖现有列表,我想删除传入资源中不再存在的实例,添加新实例,并更新现有实例。

在线医疗平台开发实战04 AutoMapper使用及无法引用问题解决

型号:

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);