如何在Mapster映射配置中注入DB Context?遇无参构造函数错误
问题:Mapster映射配置注入DbContext时触发无参构造函数缺失错误
错误信息
System.MissingMethodException: 无法动态创建类型'ChatBotCore.Application.Bots.Common.MappingConfig'的实例。原因:未定义无参数构造函数。
相关代码
public class MappingConfig : IRegister { private readonly IApplicationDbContext _context; public MappingConfig(IApplicationDbContext context) { _context = context; } public void Register(TypeAdapterConfig config) { config.NewConfig<Bot, GetBotsResult>() .Map(dest => dest.Channel, src => src.Channel.Name) .Map(dest => dest.NluServiceProvider, src => src.NluServiceProvider.Name); config.NewConfig<CreateBotCommand, Bot>() .Map(dest => dest.Channel, src => GetChannelByName(src.Channel)) .Map(dest => dest.NluServiceProvider, src => GetNluServiceProviderByName(src.NluServiceProvider)); config.NewConfig<UpdateBotCommand, Bot>() .Map(dest => dest.Channel, src => GetChannelByName(src.Channel)) .Map(dest => dest.NluServiceProvider, src => GetNluServiceProviderByName(src.NluServiceProvider)); } private async Task<NluServiceProvider> GetNluServiceProviderByName(string nluServiceProvider) { return await _context.NluServiceProviders.FirstAsync(x => x.Name.ToLower() == nluServiceProvider.ToLower()); } private async Task<Channel> GetChannelByName(string channel) { return await _context.Channels.FirstAsync(x => x.Name.ToLower() == channel.ToLower()); } }
问题分析
- 无参构造函数缺失:Mapster自动扫描
IRegister实现类时,会尝试通过无参构造函数实例化对象,但你只定义了带IApplicationDbContext参数的构造函数,导致触发异常。 - 异步方法误用:映射配置中调用了
async方法,但Mapster的同步映射逻辑不支持异步操作,会导致目标属性被赋值为Task<Channel>而非Channel,后续会引发类型不匹配错误。
解决方案
方案一:手动注册映射(推荐,逻辑更清晰)
放弃自动扫描IRegister,在DI容器初始化时手动构建映射配置,直接注入DbContext:
// Program.cs 或 Startup.cs 的服务配置段 builder.Services.AddMapster(); builder.Services.AddSingleton<IMapper>(sp => { var config = new TypeAdapterConfig(); var dbContext = sp.GetRequiredService<IApplicationDbContext>(); // Bot → GetBotsResult 映射 config.NewConfig<Bot, GetBotsResult>() .Map(dest => dest.Channel, src => src.Channel.Name) .Map(dest => dest.NluServiceProvider, src => src.NluServiceProvider.Name); // CreateBotCommand → Bot 映射(改用同步查询,避免异步问题) config.NewConfig<CreateBotCommand, Bot>() .Map(dest => dest.Channel, src => dbContext.Channels.First(x => x.Name.ToLower() == src.Channel.ToLower())) .Map(dest => dest.NluServiceProvider, src => dbContext.NluServiceProviders.First(x => x.Name.ToLower() == src.NluServiceProvider.ToLower())); // UpdateBotCommand → Bot 映射 config.NewConfig<UpdateBotCommand, Bot>() .Map(dest => dest.Channel, src => dbContext.Channels.First(x => x.Name.ToLower() == src.Channel.ToLower())) .Map(dest => dest.NluServiceProvider, src => dbContext.NluServiceProviders.First(x => x.Name.ToLower() == src.NluServiceProvider.ToLower())); return new Mapper(config); });
方案二:修改IRegister实现,通过ServiceProvider获取DbContext
如果坚持使用自动扫描,去掉构造函数,从TypeAdapterConfig的ServiceProvider属性获取DbContext:
public class MappingConfig : IRegister { public void Register(TypeAdapterConfig config) { config.NewConfig<Bot, GetBotsResult>() .Map(dest => dest.Channel, src => src.Channel.Name) .Map(dest => dest.NluServiceProvider, src => src.NluServiceProvider.Name); // CreateBotCommand → Bot 映射 config.NewConfig<CreateBotCommand, Bot>() .Map(dest => dest.Channel, src => config.ServiceProvider.GetRequiredService<IApplicationDbContext>() .Channels.First(x => x.Name.ToLower() == src.Channel.ToLower())) .Map(dest => dest.NluServiceProvider, src => config.ServiceProvider.GetRequiredService<IApplicationDbContext>() .NluServiceProviders.First(x => x.Name.ToLower() == src.NluServiceProvider.ToLower())); // UpdateBotCommand → Bot 映射 config.NewConfig<UpdateBotCommand, Bot>() .Map(dest => dest.Channel, src => config.ServiceProvider.GetRequiredService<IApplicationDbContext>() .Channels.First(x => x.Name.ToLower() == src.Channel.ToLower())) .Map(dest => dest.NluServiceProvider, src => config.ServiceProvider.GetRequiredService<IApplicationDbContext>() .NluServiceProviders.First(x => x.Name.ToLower() == src.NluServiceProvider.ToLower())); } }
然后确保DI注册时Mapster能拿到ServiceProvider:
// Program.cs 或 Startup.cs builder.Services.AddMapsterWithScanning(); // 或者手动指定扫描 builder.Services.AddMapster(config => config.Scan(typeof(MappingConfig).Assembly));
异步映射处理(如果必须用异步查询)
不要在同步映射中调用async方法,改用Mapster的异步映射API:
- 修改映射配置,使用
MapAsync:
config.NewConfig<CreateBotCommand, Bot>() .MapAsync(dest => dest.Channel, async src => await config.ServiceProvider.GetRequiredService<IApplicationDbContext>() .Channels.FirstAsync(x => x.Name.ToLower() == src.Channel.ToLower())) .MapAsync(dest => dest.NluServiceProvider, async src => await config.ServiceProvider.GetRequiredService<IApplicationDbContext>() .NluServiceProviders.FirstAsync(x => x.Name.ToLower() == src.NluServiceProvider.ToLower()));
- 在业务逻辑中调用异步映射方法:
var bot = await _mapper.From(createBotCommand).AdaptToTypeAsync<Bot>();
额外建议
- 尽量避免在映射层直接查询数据库:把数据查询逻辑放到业务服务层,映射层只负责纯对象转换,这样代码职责更清晰,也能避免DI和异步的坑。
- EF Core的
FirstAsync会在无匹配数据时抛出异常,建议根据业务需求改用FirstOrDefaultAsync并处理null场景。
内容的提问来源于stack exchange,提问作者Guillermo Verón
相关产品推荐
相关产品推荐

