在ASP.NET Core中向Automapper TypeConverter注入依赖项

4

要让DI框架将依赖项注入到Automapper自定义TypeConverter中,通常使用MapperConfiguration对象的ConstructServicesUsing方法。因此,在ASP.NET Core DI中,我期望能够像这样配置AutoMapper:

public static IMapperConfiguration Configure(IServiceProvider provider)
{
    var config = new MapperConfiguration(cfg => {
         cfg.AddProfile<MyProfile>();
         cfg.ConstructServicesUsing(type => provider.GetService(type));
    });
    config.AssertConfigurationIsValid();
    return config;
}
< p > MapperConfiguration 对象将在 startup.cs 中被配置为可注入的服务,如下所示:

public void ConfigureServices(IServiceCollection services)
{
    //other service configuration omitted for simplicity

    //Automapper config
    var provider = services.BuildServiceProvider();
    var config = AutoMapperConfig.Configure(provider);
    services.AddInstance(config);
}

在这种情况下,依赖项(即Automapper本身)将像这样注入到TypeConverter构造函数中。

public class MyConverter : ITypeConverter<ThisType, ThatType>
{
    private IMapper _mapper;

    public MyConverter(IMapperConfiguration mapperConfig)
    {
        var mc = mapperConfig as MapperConfiguration;
        _mapper = mc.CreateMapper();
    }

    public ThatType Convert(ResolutionContext context)
    {
        //do something with _mapper here
    }
}

我已经成功地在几个DI框架中使用了这个模式,但是我无法在ASP.NET Core中让它工作。猜测可能是因为Automapper需要在ConfigureServices方法完成后,给予真正的IServiceProvider实例。然而,即使我把配置的那一部分推迟到Configure方法(如下所示),依赖项仍然不能被注入到TypeConverter中。
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider provider)
{
    var config = provider.GetService<IMapperConfiguration>();
    config.ConstructServicesUsing(type => provider.GetService(type));
}

所以我的问题是:我如何在ASP.NET Core中配置Automapper,以便将依赖项注入自定义TypeConverters中?

1
你还在使用ASP.NET 5 beta7或beta8吗?我记得services.AddInstance已经被重命名为AddSingleton,以使其与其他单例注册保持一致。 - Tseng
我的错,这是在 RC2 的公告中提到的,但那已经很久以前了,我以为是在 RC1 发布之前。https://github.com/aspnet/Announcements/issues/119 - Tseng
无论如何,我都在使用RC1。 - Paul Taylor
2个回答

9
我发现解决这个问题的关键在于正确配置ConstructServicesUsing工厂方法。正如@Tseng所指出的那样,使用IServiceCollection.AddSingleton方法允许在Startup.cs的ConfigureServices方法中配置Automapper,这也是应该完成的地方:
public void ConfigureServices(IServiceCollection services)
{
    //other service configuration omitted for simplicity

    //Automapper config
    services.AddSingleton(provider => AutoMapperConfig.Configure(provider));
}

但是非常关键的一点是,Automapper必须配置使用.NET Core的ActivatorUtilities类来创建服务实例(感谢这篇文章为我提供了灵感):

public static IMapperConfiguration Configure(IServiceProvider provider)
{
    var config = new MapperConfiguration(cfg => {
         cfg.AddProfile<MyProfile>();
         cfg.ConstructServicesUsing(type => ActivatorUtilities.CreateInstance(provider, type));
    });
    config.AssertConfigurationIsValid();
    return config;
}

使用这种方法,可以配置Automapper将任何服务依赖项注入到自定义的TypeConvertersValueResolvers中。只需确保任何此类服务也添加到ConfigureServices方法中的IServiceCollection实例中即可。


2

您可以/应该使用工厂方法来注入/实例化服务。

services.AddSingleton<IMapperConfiguration>( (serviceProvider) => AutoMapperConfig.Configure(serviceProvider) );

通过这种方式,您可以推迟对Configure的调用,直到首次解析IMapperConfiguration。由于它是单例模式,它将在应用程序容器的其余部分中保持活动状态,对RequestService的进一步调用将返回相同的实例。 编辑: 尝试从您的AutoMapperConfig.Configure方法中删除config.AssertConfigurationIsValid();并添加以下代码到Startup.cs
public void Configure(IServiceProvider app) 
{
    app.VerifyAutoMapperConfig();
}

AutoMapperConfigExtensions.cs

// So one doesn't have to add namespaces to become available 
// inside Startup.cs; Recommended way all middleware register their
// AddXxx and UseXxx extension methods
namespace Microsoft.Extensions.DependencyInjection {
    public static class AutoMapperConfigExtensions
    {
        public static void VerifyAutoMapperConfig(this IServiceProvider app) 
        {
            var config = app.RequestService<IMapperConfiguration>();
            config.AssertConfigurationIsValid();
        }
    }
}

谢谢,看起来非常有前途。不幸的是,当我尝试使用TypeConverter时,我遇到了相同的错误:MyConverter需要具有0个参数或仅可选参数的构造函数\r\n参数名称:类型。我想知道AM 4.2是否像以前的版本一样使用IServiceProvider引用来注入依赖项。 - Paul Taylor
我怀疑你在这里存在循环依赖。你的 MyConverter 依赖于 IMapperConfiguration,但是 IMapperConfiguration 只能在调用 AutoMapperConfig.Configure 后才能解析,但由于你在 configure 方法内部调用了 config.AssertConfigurationIsValid();,它会在返回之前尝试解析它。尝试将此调用移动到 Configure 方法之外,即 Startup.cs 的 Configure(IServiceProvider app) 方法内部。我会在答案中更新一个示例。 - Tseng
不幸的是,这也没有解决问题。顺便说一下,当我调试代码时,我可以看到AutomapperConfig.Configure方法直接从Startup.cs中添加的服务配置中调用。它不会像你提到的那样被推迟到第一次使用。这让我想知道我是否在某个地方做错了什么。 - Paul Taylor
另外,我尝试用另一个可注入的服务替换MyConverterIMapperConfiguration依赖项,但是得到了相同的错误,因此我认为这排除了循环依赖问题。 - Paul Taylor
provider.GetService(type)cfg.ConstructServicesUsing(type => provider.GetService(type)); 中被调用了吗?如果没有,它可能正在使用错误的 IMapperConfiguration 实例,而不是您配置的实例。您没有正确使用它的静态实例吗? - Tseng
不,我正在使用推荐的 AM 4.2 方法。不过我已经发现了问题。我会很快撰写说明供其他人参考。 - Paul Taylor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接