使用AutoMapper将两个外部API返回结果映射合并为单个DTO对象
C# AutoMapper 双接口数据匹配合并方案
问题根因
原有逻辑的核心问题是:AutoMapper默认不知道如何按User.Id = Album.UserId的规则匹配两个集合的对应关系,你定义的Album到UserDetailDTO的映射规则本身不符合业务逻辑——你需要的是将匹配的Album集合注入到UserDetailDTO.Albums属性,而非用Album覆盖整个UserDetailDTO对象。
调整方案
方案一:手动赋值(更简单直观,无复杂AutoMapper配置)
步骤1:简化AutoMapper配置
删掉无效的Album到UserDetailDTO的映射,仅保留基础的User到UserDetailDTO映射:
public MappingProfile() { CreateMap<User, UserDetailDTO>(); }
同时建议给UserDetailDTO加默认初始化逻辑,避免空引用:
public class UserDetailDTO { public int Id { get; set; } public string Name { get; set; } public string Username { get; set; } // 初始化空集合 public List<Album> Albums { get; set; } = new List<Album>(); }
步骤2:调整服务层映射逻辑
先将Album按UserId分组生成查找表,再遍历用户DTO匹配注入对应专辑:
var users = JsonConvert.DeserializeObject<List<User>>(usersResponseContent); var albums = JsonConvert.DeserializeObject<List<Album>>(albumsResponseContent); // 按UserId对专辑分组,查找性能O(1) var albumsLookup = albums.ToLookup(al => al.UserId); // 先完成User到UserDetailDTO的基础映射 var mappedUsers = _mapper.Map<List<UserDetailDTO>>(users); // 遍历注入匹配的专辑集合 foreach(var user in mappedUsers) { user.Albums = albumsLookup[user.Id].ToList(); }
方案二:AutoMapper上下文传参(全映射实现)
如果不想写手动遍历的代码,可以通过AutoMapper的上下文参数传递专辑分组,配置映射规则自动注入:
步骤1:调整AutoMapper配置
public MappingProfile() { CreateMap<User, UserDetailDTO>() .ForMember(dest => dest.Albums, opt => opt.MapFrom((_, dest, _, context) => { var albumsLookup = context.Items["AlbumsLookup"] as ILookup<int, Album>; return albumsLookup?[dest.Id].ToList() ?? new List<Album>(); })); }
步骤2:服务层传参调用映射
var users = JsonConvert.DeserializeObject<List<User>>(usersResponseContent); var albums = JsonConvert.DeserializeObject<List<Album>>(albumsResponseContent); var albumsLookup = albums.ToLookup(al => al.UserId); // 映射时传入专辑分组参数 var mappedUsers = _mapper.Map<List<UserDetailDTO>>(users, opt => { opt.Items["AlbumsLookup"] = albumsLookup; });
内容的提问来源于stack exchange,提问作者BlockeeR
相关产品推荐
相关产品推荐

