【问题标题】:Injecting dependencies to an Automapper TypeConverter in ASP.NET Core将依赖项注入到 ASP.NET Core 中的 Automapper TypeConverter
【发布时间】:2016-09-13 23:13:37
【问题描述】:

要让 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;
}

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 方法完成后获得由 .NET 构建的真实 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?

【问题讨论】:

  • 您还在使用 ASP.NET 5 beta7 或 beta8 吗?我记得 services.AddInstance 已重命名为 AddSingleton 以使其与其他单例注册保持一致
  • 我的错,这是在 RC2 的公告中,但那是很久以前的事了,我以为是在 RC1 发布之前。 github.com/aspnet/Announcements/issues/119
  • 无论如何我都在使用 RC1。

标签: dependency-injection asp.net-core automapper


【解决方案1】:

我发现这个问题的解决方法在于正确配置ConstructServicesUsing工厂方法。正如@Tseng 所指出的,使用IServiceCollection.AddSingleton 方法可以让Automapper 在Startup.cs 的ConfigureServices 方法中进行配置,这是应该做的:

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

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

但至关重要的是,必须将 Automapper 配置为使用 .NET Core 的 ActivatorUtilities 类来创建服务实例(感谢 this article 给我的想法):

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 needs to have a constructor with 0 args or only optional args\r\nParameter name: type。我想知道 AM 4.2 是否使用 IServiceProvider 引用来注入与以前版本相同的依赖项。
    • 我怀疑你在这里有一个循环依赖。您的MyConverter 依赖于IMapperConfiguration,但IMapperConfiguration 只能在调用AutoMapperConfig.Configure 之后解决,但由于您在configure 内部调用config.AssertConfigurationIsValid();,它会尝试在返回之前解决它。尝试将此调用移到 Configure 方法之外,即在 Startup.cs 的 Configure(IServiceProvider app) 方法中。我会用一个例子来更新答案
    • 不幸的是,这也没有解决它。顺便说一句,当我调试代码时,我可以看到 AutomapperConfig.Configure 方法是直接从 Startup.cs 中添加的服务配置调用的。正如您提到的,它不会推迟到第一次使用。这让我想知道我是否在某个地方做错了什么。
    • 另外,我尝试用另一个可注入服务替换 MyConverterIMapperConfiguration 依赖,但得到了同样的错误,所以我认为这排除了循环依赖问题。
    • provider.GetService(type) 是否在cfg.ConstructServicesUsing(type =&gt; provider.GetService(type)); 中被调用?如果不是,它可能使用了错误的 IMapperConfiguration 实例而不是您配置的实例。您没有使用它的静态实例吗?
    猜你喜欢
    • 2023-03-12
    • 1970-01-01
    • 2019-12-19
    • 1970-01-01
    • 2018-01-16
    • 2019-02-22
    • 1970-01-01
    • 1970-01-01
    • 2017-09-10
    相关资源
    最近更新 更多