使用AutoMapper映射含构造函数与嵌套列表的类遇问题求助
我有两个实体类User和UserQuota,其中User包含一个配额列表。需要将这些类映射到DTO对象UserWebDtoWithQuotas和UserQuotaWebDto,UserWebDtoWithQuotas也包含UserQuotaWebDto的列表。
我倾向于使用严格构造函数定义类,DTO代码如下:
public record UserWebDtoWithQuotas { public Guid UserId { get; private init; } public string Username { get; private init; } public List<UserQuotaWebDto> Quotas { get; private init; } public UserWebDtoWithQuotas(Guid userId, string username, List<UserQuotaWebDto> quotas) { UserId = userId; Username = username; Quotas = quotas; } }
我尝试定义以下两条映射规则:
CreateMap<User, UserWebDtoWithQuotas>(); CreateMap<UserQuota, UserQuotaWebDto>();
但抛出错误:Type 'Application.Operations.Users.DTOs.UserWebDtoWithQuotas' does not have a default constructor (Parameter 'type')。
我尝试用.ConvertUsing单行代码映射时可以正常工作:
CreateMap<User, UserWebDtoWithQuotas>() .ConvertUsing(user => new UserWebDtoWithQuotas(user.Id, user.Username, new List<UserQuotaWebDto>()));
但当我尝试映射嵌套列表,改用带上下文的ConstructUsing时,再次抛出相同的“无默认构造函数”错误:
CreateMap<User, UserWebDtoWithQuotas>() .ConstructUsing((user, context) => { var quotas = context.Mapper.Map<List<UserQuotaWebDto>>(user.Quotas); return new UserWebDtoWithQuotas(user.Id, user.Username, quotas); });
请问我哪里出错了?是否可以为对象构造设置复杂的自定义规则?
问题根源
使用ConstructUsing的带ResolutionContext重载时,AutoMapper内部仍会尝试验证目标类型是否存在默认构造函数,即使你已经提供了自定义构造逻辑。而ConvertUsing的单行版本之所以能工作,是因为它完全接管了映射流程,跳过了AutoMapper的默认构造检查环节。
解决方法1:改用带上下文的ConvertUsing重载
将ConstructUsing替换为ConvertUsing的带ResolutionContext参数版本,完全自定义映射逻辑,避免触发默认构造检查:
CreateMap<User, UserWebDtoWithQuotas>() .ConvertUsing((user, context) => { var quotas = context.Mapper.Map<List<UserQuotaWebDto>>(user.Quotas); return new UserWebDtoWithQuotas(user.Id, user.Username, quotas); });
解决方法2:简化Record定义(推荐)
C#的Record类型支持主构造函数语法,可以省略显式的构造函数和属性初始化代码,AutoMapper能够自动识别并匹配主构造函数的参数(按名称匹配)。修改DTO定义如下:
public record UserWebDtoWithQuotas(Guid UserId, string Username, List<UserQuotaWebDto> Quotas);
此时只需保留基础的映射配置即可正常工作,无需额外的ConstructUsing或ConvertUsing:
CreateMap<User, UserWebDtoWithQuotas>(); CreateMap<UserQuota, UserQuotaWebDto>();
AutoMapper会自动将User.Id映射到UserId、User.Username映射到Username、User.Quotas映射到Quotas,同时正确处理嵌套列表的映射。
内容的提问来源于stack exchange,提问作者Valhaal

