【问题标题】:Specify custom IModelBinderProvider without new instance of IServiceProvider?指定没有 IServiceProvider 新实例的自定义 IModelBinderProvider?
【发布时间】:2021-03-09 02:22:50
【问题描述】:

我的Startup 类中有以下代码,它使用IModelBinderProvider 的自定义实例。大多数输入格式化程序需要一个ILogger 实例,所以我需要一个ILoggerFactory,为此我想使用配置的,它以指定的详细程度记录到指定的目标。我正在从新建的IServiceProvider 中获取ILoggerFactory

    public void ConfigureServices(IServiceCollection services)
    {
        ...
    
        services.AddMvcCore(
            options =>
            {
                ...
            
                var serviceProvider = services.BuildServiceProvider();
                var loggerFactory = serviceProvider.GetService<ILoggerFactory>();

                options.ModelBinderProviders.Clear();
                options.ModelBinderProviders.Add(
                    new MyCustomBinderProvider(options.InputFormatters, loggerFactory)
                );
                
                ...
            }
        );
        
        ...
    }

问题是我收到以下警告:

Startup.cs(62, 43): [ASP0000] Calling 'BuildServiceProvider' from application code results in an
                    additional copy of singleton services being created. Consider alternatives such
                    as dependency injecting services as parameters to 'Configure'.

我看了一下这个问题:Resolving instances with ASP.NET Core DI from within ConfigureServices。但是,我的代码是 AddMvcCore 的 lambda,它正在配置 options 对象。换句话说,在调用Configure 时,MVC 选项已经定义好了。

有没有办法在这里做正确的事情,即防止IServiceProvider 的无关实例?

【问题讨论】:

  • 谢谢@Nkosi。我的自定义绑定提供程序受到BodyModelBinderProvider 的启发,它接受ILoggerFactory 作为输入。事实上,BodyModelBinderProvider 的实例已经存在,我不得不调用Clear 的原因。如果 ASP.NET Core 能够及早解决 logger factory,我想我应该也可以这样做...

标签: asp.net-mvc asp.net-core dependency-injection asp.net-core-mvc asp.net-core-3.1


【解决方案1】:

在配置选项时有几种方法可以注入服务。第一种方法是使用OptionsBuilder&lt;TOptions&gt;,可以通过扩展方法IServiceCollection.AddOptions获得,如下所示:

services.AddOptions<MvcOptions>()
         //first arg is always of TOptions, 
         //injectable dependencies start from the second arg
        .Configure((MvcOptions o, ILoggerFactory loggerFactory) => {
                       o.ModelBinderProviders.Insert(0,
                          new MyCustomBinderProvider(o.InputFormatters, loggerFactory)
                       );
                   });

第二种方法是实现IConfigureOptions&lt;TOptions&gt;(或IPostConfigureOptions&lt;TOptions&gt;),如下所示:

public class ConfigureMvcOptions : IConfigureOptions<MvcOptions>
{
    readonly ILoggerFactory _loggerFactory;
    public ConfigureMvcOptions(ILoggerFactory loggerFactory)
    {
        _loggerFactory = loggerFactory;
    }
    public void Configure(MvcOptions options)
    {
        options.ModelBinderProviders.Insert(0,
                  new MyCustomBinderProvider(options.InputFormatters, _loggerFactory)
               );
    }
}

//then configure it like this:
services.ConfigureOptions<ConfigureMvcOptions>();

第一种方法很方便,因为您不必为实现IConfigureOptions&lt;Options&gt; 创建单独的类,但它的可注入依赖项数量有限。第二种方式可以注入任意数量,适合大量配置代码。

【讨论】:

    【解决方案2】:

    您无需构建 ServiceProvider 即可解析 ILoggerFactory 以将其作为参数传递。

    在您的 MyCustomBinderProvider 内部,您可以访问解决 ILoggerFactory,我认为您应该有这样的东西

    public class MyCustomBinderProvider : IModelBinderProvider
    {
        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context.Metadata.ModelType == typeof(CustomDto))
            {
                var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
                return new MyCustomBinder(logger);
            }
    
            return null;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-11
      • 2021-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多