在 实现 ServiceCollection extension methods 时,如果需要 bind Options,通常的做法是增加 IConfiguration 类型的参数,让调用方传入。
为了追求代码的简洁,我们有时希望减少这个传参操作,于是直接在扩展方法中获取 IConfiguration,从而误入歧途,详见 https://q.cnblogs.com/q/142496/
var config = services.BuildServiceProvider().GetRequiredService<IConfiguration>();
为了避开上面的坑,只能弃用 BuildServiceProvider,但我们又不想修改所有调用方的代码,让其传入 IConfiguration。
public static IServiceCollection AddEnyimMemcached(
this IServiceCollection services,
IConfiguration configuration,
string sectionKey = "enyimMemcached",
bool asDistributedCache = true)
{
var section = configuration.GetSection(sectionKey);
return services.AddEnyimMemcachedInternal(
s => s.Configure<MemcachedClientOptions>(section), asDistributedCache);
}
传入 IConfiguration 的目的就是为了绑定上面的 MemcachedClientOptions,如果我们能够实现不需要调方传入而通过 Dependency Injection 容器解析出 IConfiguration,就能实现既能避开 BuildServiceProvider 的坑又不需要调用方修改任何代码的美好愿望。
请问如何实现?
services.AddOptions<MemcachedClientOptions>().BindConfiguration(sectionKey);
赞!蜻蜓点水般地解决了问题
如果不是你的回答及时解救,我差点尝试采用 IConfigureOptions<TOptions>
解决这个问题,就是 How To Use The Options Pattern In ASP.NET Core 7 这篇博文中的方法
@dudu: 他们的底层其实都是一样的, 最终都是 AddSingleton<IConfigureOptions<TOptions>>, 2条路, 一模一样的目的地.
实现自己的IConfigureOptions 有更复杂的绑定逻辑的时候用可能更合适.
今天在写代码时的进一步使用
public static IHttpClientBuilder AddBlogClient(
this IServiceCollection services,
string configName = "blogClient",
Action<BlogClientOptions>? configureOptions = null)
{
services.AddOptions<BlogClientOptions>()
.BindConfiguration(configName)
.PostConfigure(options => configureOptions?.Invoke(options));
services.AddTransient<BlogClient>();
return services.AddRefitClient<IBlogPostClient>()
.ConfigureHttpClient((sp, client) =>
client.BaseAddress = new Uri(
sp.GetRequiredService<IOptions<BlogClientOptions>>().Value.BaseAddress));
}