我需要在
AutoMapper
中的 ASP.NET Core
配置文件类中使用服务层,但是当我在构造函数中注入服务时,它不起作用。例如:
public class UserProfile : Profile
{
private readonly IUserManager _userManager;
public UserProfile(IUserManager userManager)
{
_userManager = userManager;
CreateMap<User, UserViewModel>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => $"{src.FirstName} {src.LastName}"));
}
}
在
Startup
班级:
public class Startup
{
public IConfigurationRoot Configuration { set; get; }
public Startup(IHostingEnvironment env)
{
//some code
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddMvc();
services.AddScoped<IUsersPhotoService, UsersPhotoService>();
services.AddAutoMapper(typeof(UserProfile));
}
}
如何做?
要解决您的问题,您只需在 DI 中连接
IUserManager
,并确保 UserProfile
依赖性已解决。
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddSingleton<IUserManager, UserManager>();
services.AddSingleton(provider => new MapperConfiguration(cfg =>
{
cfg.AddProfile(new UserProfile(provider.GetService<IUserManager>()));
}).CreateMapper());
}
话虽如此,我可能会尝试保持每个类的单一责任,并且不将任何服务注入到映射配置文件中。您可以在映射之前填充对象。这样单元测试也可能更容易。
为此目的最好使用自定义 IValueResolver,因为它完全支持 IServiceCollection 集成(使用 AutoMapper.Extensions.Microsoft.DependencyInjection)。
您可能需要实现自定义值解析器:
public class UserViewModelValueResolver: IValueResolver<...>
{
public readonly IUserManager userManager;
public UserViewModelValueResolver(IUserManager userManager)
{
this.userManager = userManager;
}
...
}
服务中的注册可能会减少为:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddSingleton<IUserManager, UserManager>();
}
然后您可以通过构造函数注入
IMapper
来获取控制器内的映射器实例。
您可以使用 IMappingAction 在映射器中注入服务
public class SetSomeAction : IMappingAction<SomeModel, SomeOtherModel>
{
private readonly IService service;
public SetSomeAction(IService _service)
{
service = _service;
}
public void Process(SomeModel source, SomeOtherModel destination, ResolutionContext context)
{
//here you can use the service
}
}
在自动映射器配置文件中像这样连接它:
public class SomeProfile : Profile
{
public SomeProfile()
{
CreateMap<SomeModel, SomeOtherModel>()
.AfterMap<SetSomeAction>();
//Here just connect IMappingAction with profile
}
}
我知道这个问题不是最近提出的,但有一个 nuget 包:AutoMapperBuilder。
您可以通过替换此行来获得您想要的内容:
services.AddAutoMapper(typeof(UserProfile));
与这些:
services.AddAutoMapperBuilder(builder =>
{
builder.Profiles.Add(new UserProfile(services.BuildServiceProvider().GetRequiredService<IUserManager>()));
});
基于 Ignas 的解决方案,使用最新的
AutoMapper.Extensions.Microsoft.DependencyInjection
,您现在还可以使用 Action<IMapperConfigurationExpression>
或 Action<IServiceProvider, IMapperConfigurationExpression>
重载进行一些简化,具体取决于您是否需要服务提供商来解决其他依赖项。
尽管后者存在
params Assembly[]
和 params Type[]
的不明确重载,并且您还必须指定定义映射器配置的程序集或程序集中的类型,这对于解析任何依赖的 IValueResolver 或其他 Automapper 非常重要配置文件使用的类型。
示例:
可以将以下内容添加到启动中,并将域详细信息隐藏在单独程序集中的另一个服务扩展类中:
// Startup.cs
// // IServicesCollection services
// ...
services.AddAutoMapper((serviceProvider, mapperConfiguration) =>
{
services.RegisterYourProfiles(serviceProvider, mapperConfiguration);
}, services.GetYourProfileAssembly();
// In another extension method in your domain assembly:
public static void RegisterYourMapperProfiles(this IServiceCollection services, IServiceProvider serviceProvider,
IMapperConfigurationExpression mapperConfiguration)
{
// You can use the serviceProvider to resolve any dependency of your
// custom profile classes that need to be part of DI
mapperConfiguration.AddProfile(new CustomMappingProfile());
}
public static Assembly GetYourProfileAssembly(this IServiceCollection services)
{
return typeof(CustomMappingProfile).Assembly;
}
每个域模块/程序集都可以有自己的扩展方法来抽象映射器类的详细信息。
如果您不需要 serviceProvider,您可以选择仅传递
IMapperConfigurationExpression
的更简单的重载:
// IServicesCollection services
services.AddAutoMapper((mapperConfiguration) =>
{
services.RegisterYourProfiles(mapperConfiguration);
},
services.GetYourProfileAssembly();
);
写完这篇文章后,这是否比 Ignas 建议的手动设置更简单还有待商榷,但这是一种使用 AddAutoMapper 并仍然允许 DI 的方法。
我也同意最好保持您的配置文件干净且不受其他依赖项的影响,但是对于您希望不受 Automapper 框架依赖项影响的某些目标字段,可能有一个源 -> 目标映射策略,并且可以自由地通过接口和 DI 实现即插即用风格。