ASP.NET Core中StructureMap与AutoMapper解析器依赖注入异常问题
我帮你排查下这个问题!你遇到的AutoMapperMappingException: Cannot create an instance of type AutomapperProfiles.UserRolesResolver异常,核心原因是AutoMapper没有拿到正确的依赖注入容器来解析你的自定义解析器,结合你用StructureMap作为DI容器的场景,咱们一步步来解决:
1. 确保解析器和依赖服务都注册到StructureMap
StructureMap不会自动识别所有自定义类型,你得确保UserRolesResolver和它依赖的AppUserManager都被正确注册到容器里。推荐用扫描程序集的方式批量注册,省得手动逐个添加:
// 在StructureMap的配置代码中 container.Configure(config => { config.Scan(scan => { scan.TheCallingAssembly(); // 扫描当前程序集 scan.WithDefaultConventions(); // 注册所有符合默认约定的类型 scan.AddAllTypesOf<IValueResolver>(); // 自动注册所有实现IValueResolver的类,包括你的UserRolesResolver }); // 如果AppUserManager是自定义服务,确保它也被注册: config.For<AppUserManager>().Use<AppUserManager>(); });
2. 让AutoMapper用StructureMap容器构造服务
你之前在AddAutoMapper里用了x.ConstructServicesUsing(type=> ServiceProvider.GetRequiredService(type)),但这里的ServiceProvider大概率是ASP.NET Core默认的DI容器,而非你配置的StructureMap容器,所以AutoMapper找不到解析器的实例。
正确的做法是,在StructureMap容器初始化完成后,给AutoMapper指定用StructureMap来构造服务:
public IServiceProvider ConfigureServices(IServiceCollection services) { // 先添加AutoMapper基础服务 services.AddAutoMapper(typeof(OrderProfile).Assembly); // 初始化StructureMap容器 var container = new Container(); container.Configure(config => { // 上面的注册代码... config.Populate(services); // 把ASP.NET Core的服务导入到StructureMap }); // 关键:让AutoMapper使用StructureMap容器来解析服务 var mapperConfig = container.GetInstance<MapperConfiguration>(); mapperConfig.ConstructServicesUsing(type => container.GetInstance(type)); // 返回StructureMap的ServiceProvider替换默认DI return container.GetInstance<IServiceProvider>(); }
3. 验证映射配置是否正确
最后检查你的Profile中是否正确引用了这个解析器,比如:
public class UserProfile : Profile { public UserProfile() { CreateMap<User, CurrentUserViewModel>() .ForMember(dest => dest.Roles, opt => opt.MapFrom<UserRolesResolver>()); } }
额外优化建议
你在解析器里用AsyncHelpers.RunSync来调用异步方法,其实AutoMapper支持异步解析器IAsyncValueResolver,可以避免同步阻塞:
public class UserRolesResolver : IAsyncValueResolver<User, CurrentUserViewModel, List<string>> { private readonly AppUserManager _appUserManager; public UserRolesResolver(AppUserManager appUserManager) { _appUserManager = appUserManager; } public async Task<List<string>> ResolveAsync(User source, CurrentUserViewModel destination, List<string> destMember, ResolutionContext context) { var roles = await _appUserManager.GetRolesAsync(source); destination.Roles = roles.ToList(); return destination.Roles; } }
然后在映射时使用ResolveUsingAsync:
CreateMap<User, CurrentUserViewModel>() .ForMember(dest => dest.Roles, opt => opt.ResolveUsingAsync<UserRolesResolver>());
这样代码更符合异步编程的最佳实践~
内容的提问来源于stack exchange,提问作者mohammadali ghanbari

