AutoMapper映射C#记录类型报错:目标类型需无参或可选构造函数
解决AutoMapper映射Record类型时的构造函数错误
问题场景
需要将以下Request Record类型映射到对应的Command Record:
源类型:
public record CreateBulletinRequest(string Text, int Rating, DateTimeOffset Expiry, Guid UserId); public record UpdateBulletinRequest(string Text, int Rating, DateTimeOffset Expiry, Guid UserId);
目标类型:
public record CreateBulletinCommand(string Text, int Rating, DateTime ExpiryUtc, Guid UserId); public record UpdateBulletinCommand(string Text, int Rating, DateTime ExpiryUtc, Guid UserId);
配置AutoMapper映射Profile后:
public class BulletinMappingProfile : Profile { public BulletinMappingProfile() { CreateMap<CreateBulletinRequest, CreateBulletinCommand>() .ForMember( dest => dest.ExpiryUtc, opt => opt.MapFrom(src => src.Expiry)); CreateMap<UpdateBulletinRequest, UpdateBulletinCommand>() .ForMember( dest => dest.ExpiryUtc, opt => opt.MapFrom(src => src.Expiry)); } }
运行时抛出错误:
UpdateBulletinCommand needs to have a constructor with 0 args or only optional args. Validate your configuration for details. (Parameter 'type')
尝试添加全局类型转换规则后问题仍未解决:
CreateMap<DateTimeOffset, DateTime>().ConvertUsing(src => src.UtcDateTime);
原因分析
AutoMapper处理C# Record类型时,默认优先使用主构造函数实例化对象。当目标Record的主构造函数参数与源类型的属性名称/类型不直接匹配(比如这里的Expiry(DateTimeOffset)→ExpiryUtc(DateTime)),AutoMapper无法自动匹配构造函数参数,就会抛出需要无参构造的错误。
解决方案
方案1:给目标Record添加无参构造函数
C# Record默认没有无参构造,需通过次级构造函数显式定义:
public record UpdateBulletinCommand(string Text, int Rating, DateTime ExpiryUtc, Guid UserId) { // 显式添加无参构造,调用主构造传入默认值 public UpdateBulletinCommand() : this(string.Empty, 0, DateTime.MinValue, Guid.Empty) { } }
配置无需修改,AutoMapper会使用无参构造创建实例后通过属性赋值完成映射。
方案2:手动指定构造函数映射(推荐)
直接控制构造逻辑,跳过AutoMapper的自动构造匹配,同时处理类型转换:
public class BulletinMappingProfile : Profile { public BulletinMappingProfile() { CreateMap<CreateBulletinRequest, CreateBulletinCommand>() .ConstructUsing(src => new CreateBulletinCommand( src.Text, src.Rating, src.Expiry.UtcDateTime, src.UserId)); CreateMap<UpdateBulletinRequest, UpdateBulletinCommand>() .ConstructUsing(src => new UpdateBulletinCommand( src.Text, src.Rating, src.Expiry.UtcDateTime, src.UserId)); } }
这种方式直观可控,无需修改Record定义。
方案3:配置构造函数参数映射
保留全局类型转换,同时指定目标构造函数参数的对应关系:
public class BulletinMappingProfile : Profile { public BulletinMappingProfile() { // 全局类型转换规则 CreateMap<DateTimeOffset, DateTime>().ConvertUsing(src => src.UtcDateTime); CreateMap<CreateBulletinRequest, CreateBulletinCommand>() .ForCtorParam("ExpiryUtc", opt => opt.MapFrom(src => src.Expiry)); CreateMap<UpdateBulletinRequest, UpdateBulletinCommand>() .ForCtorParam("ExpiryUtc", opt => opt.MapFrom(src => src.Expiry)); } }
ForCtorParam会告诉AutoMapper目标构造函数的ExpiryUtc参数从源的Expiry属性获取,结合全局转换完成类型映射。
内容的提问来源于stack exchange,提问作者tarioma
相关产品推荐
相关产品推荐

