指定没有 IServiceProvider 新实例的自定义 IModelBinderProvider?
Posted
技术标签:
【中文标题】指定没有 IServiceProvider 新实例的自定义 IModelBinderProvider?【英文标题】:Specify custom IModelBinderProvider without new instance of IServiceProvider? 【发布时间】: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,我想我应该也可以这样做...
【参考方案1】:
在配置选项时有几种方法可以注入服务。第一种方法是使用OptionsBuilder<TOptions>
,可以通过扩展方法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<TOptions>
(或IPostConfigureOptions<TOptions>
),如下所示:
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<Options>
创建单独的类,但它的可注入依赖项数量有限。第二种方式可以注入任意数量,适合大量配置代码。
【讨论】:
【参考方案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;
【讨论】:
以上是关于指定没有 IServiceProvider 新实例的自定义 IModelBinderProvider?的主要内容,如果未能解决你的问题,请参考以下文章
解决ASP.NET Core在Task中使用IServiceProvider的问题
解决ASP.NET Core在Task中使用IServiceProvider的问题