如何在 AutoMapper 配置文件类中注入服务

问题描述 投票:0回答:5

我需要在

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));
    }
}

如何做?

c# asp.net-core automapper
5个回答
92
投票

要解决您的问题,您只需在 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());
}

话虽如此,我可能会尝试保持每个类的单一责任,并且不将任何服务注入到映射配置文件中。您可以在映射之前填充对象。这样单元测试也可能更容易。


23
投票

为此目的最好使用自定义 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
来获取控制器内的映射器实例。

基于:AutoMapper:使用自定义值解析器处理配置文件依赖关系 - 技术网


4
投票

您可以使用 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
        }
}

1
投票

我知道这个问题不是最近提出的,但有一个 nuget 包:AutoMapperBuilder

您可以通过替换此行来获得您想要的内容:

services.AddAutoMapper(typeof(UserProfile));

与这些:

services.AddAutoMapperBuilder(builder =>
{
     builder.Profiles.Add(new UserProfile(services.BuildServiceProvider().GetRequiredService<IUserManager>()));
});

0
投票

基于 Ignas 的解决方案,使用最新的

AutoMapper.Extensions.Microsoft.DependencyInjection
,您现在还可以使用
Action<IMapperConfigurationExpression>
Action<IServiceProvider, IMapperConfigurationExpression>
重载进行一些简化,具体取决于您是否需要服务提供商来解决其他依赖项。

尽管后者存在

params Assembly[]
params Type[]
的不明确重载,并且您还必须指定定义映射器配置的程序集或程序集中的类型,这对于解析任何依赖的 IValueResolver 或其他 Automapper 非常重要配置文件使用的类型。

另请参阅https://github.com/AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjection/blob/master/src/AutoMapper.Extensions.Microsoft.DependencyInjection/ServiceCollectionExtensions.cs

示例:

可以将以下内容添加到启动中,并将域详细信息隐藏在单独程序集中的另一个服务扩展类中:


// 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 实现即插即用风格。

© www.soinside.com 2019 - 2024. All rights reserved.