IOptions注入

Dar*_*g8r 23 c# asp.net-core

在我看来,让域服务需要一个IOptions实例来传递它配置是一个坏主意.现在我已经将额外的(不必要的?)依赖项拉入库中.我已经看到很多在网络上注入IOse的例子,但是我没有看到它带来的额外好处.

为什么不将实际的POCO注入服务?

    services.AddTransient<IConnectionResolver>(x =>
    {
        var appSettings = x.GetService<IOptions<AppSettings>>();

        return new ConnectionResolver(appSettings.Value);
    });
Run Code Online (Sandbox Code Playgroud)

甚至使用这种机制:

        AppSettings appSettings = new AppSettings();

        Configuration.GetSection("AppSettings").Bind(appSettings);

        services.AddTransient<IConnectionResolver>(x =>
        {      
             return new ConnectionResolver(appSettings.SomeValue);
        });
Run Code Online (Sandbox Code Playgroud)

使用设置:

public class MyConnectionResolver 
{
     // Why this?
     public MyConnectionResolver(IOptions<AppSettings> appSettings)
     {
           ... 
     }

     // Why not this?
     public MyConnectionResolver(AppSettings appSettings)
     {
           ... 
     }

     // Or this
     public MyConnectionResolver(IAppSettings appSettings)
     {
           ... 
     }
}
Run Code Online (Sandbox Code Playgroud)

为什么附加依赖?IOptions给我带来了什么而不是旧式的注入方式?

Tse*_*eng 17

从技术上讲,没有什么可以阻止您使用ASP.NET Core的依赖注入注册POCO类或创建包装类并IOption<T>.Value从中返回.

但是,你将失去选件包的高级功能,即让他们自动更新,当源的变化,你可以在源看这里.

正如您在该代码示例中所看到的,如果您通过services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));它注册选项,则会将appsettings.json中的设置读取并绑定到模型中,并另外跟踪它以进行更改.编辑appsettings.json时,将使用此处显示的新值重新绑定模型.

当然,如果您想将一些基础设施泄漏到您的域中或传递该Microsoft.Extension.Options软件包提供的额外功能,您需要自己决定.它是一个非常小的包,它与ASP.NET Core无关,因此可以独立使用它.

Microsoft.Extension.Options包足够小,只包含抽象和具体的services.Configure重载IConfiguration(与配置的获取方式,命令行,json,环境,azure密钥库等更紧密相关)是一个单独的包.

总而言之,它对"基础设施"的依赖性非常有限.

  • AFAIK,IOptions 无法正常重新加载,您需要使用 IOptionsMonitor 或 IOptionsSnapshot https://andrewlock.net/reloading-strongly-typed-options-in-asp-net-core-1-1-0/ (4认同)

Sha*_*ani 15

为了避免构造函数污染IOptions<>

startup.cs通过里面的这两行简单的行,ConfigureServices您可以注入IOptions如下值:

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
    services.AddScoped(cfg => cfg.GetService<IOptions<AppSettings>>().Value);
}
Run Code Online (Sandbox Code Playgroud)

然后使用:

 public MyService(AppSettings appSettings)
 {
       ... 
 }
Run Code Online (Sandbox Code Playgroud)

信用


Aar*_*ton 6

虽然使用IOption是官方的做事方式,但我似乎无法超越这样一个事实:我们的外部库不需要了解有关 DI 容器或其实现方式的任何信息。IOption似乎违反了这个概念,因为我们现在告诉我们的类库一些关于 DI 容器注入设置的方式 - 我们应该只注入由该类定义的 POCO 或接口。

这让我非常恼火,以至于我编写了一个实用程序来将 POCO 注入到我的类库中,其中填充了 appSettings.json 部分中的值。将以下类添加到您的应用程序项目中:

public static class ConfigurationHelper
{
    public static T GetObjectFromConfigSection<T>(
        this IConfigurationRoot configurationRoot,
        string configSection) where T : new()
    {
        var result = new T();

        foreach (var propInfo in typeof(T).GetProperties())
        {
            var propertyType = propInfo.PropertyType;
            if (propInfo?.CanWrite ?? false)
            {
                var value = Convert.ChangeType(configurationRoot.GetValue<string>($"{configSection}:{propInfo.Name}"), propInfo.PropertyType);
                propInfo.SetValue(result, value, null);
            }
        }

        return result;

    }
}
Run Code Online (Sandbox Code Playgroud)

可能还可以进行一些增强,但是当我使用简单的字符串和整数值对其进行测试时,它效果很好。DataStoreConfiguration下面是我在应用程序项目的 Startup.cs -> ConfigureServices 方法中对名为的设置类和同名的 appSettings.json 部分使用此方法的示例:

services.AddSingleton<DataStoreConfiguration>((_) =>
    Configuration.GetObjectFromConfigSection<DataStoreConfiguration>("DataStoreConfiguration"));
Run Code Online (Sandbox Code Playgroud)

appSettings.json 配置如下所示:

{
  "DataStoreConfiguration": {
    "ConnectionString": "Server=Server-goes-here;Database=My-database-name;Trusted_Connection=True;MultipleActiveResultSets=true",
    "MeaningOfLifeInt" : "42"
  },
 "AnotherSection" : {
   "Prop1" : "etc."
  }
}
Run Code Online (Sandbox Code Playgroud)

该类DataStoreConfiguration是在我的库项目中定义的,如下所示:

namespace MyLibrary.DataAccessors
{
    public class DataStoreConfiguration
    {
        public string ConnectionString { get; set; }
        public int MeaningOfLifeInt { get; set; }
    }
}
Run Code Online (Sandbox Code Playgroud)

通过此应用程序和库配置,我可以使用构造函数注入将 DataStoreConfiguration 的具体实例直接注入到我的库中,而无需包装IOption器:

using System.Data.SqlClient;

namespace MyLibrary.DataAccessors
{
    public class DatabaseConnectionFactory : IDatabaseConnectionFactory
    {

        private readonly DataStoreConfiguration dataStoreConfiguration;

        public DatabaseConnectionFactory(
            DataStoreConfiguration dataStoreConfiguration)
        {
            // Here we inject a concrete instance of DataStoreConfiguration
            // without the `IOption` wrapper.
            this.dataStoreConfiguration = dataStoreConfiguration;
        }

        public SqlConnection NewConnection()
        {
            return new SqlConnection(dataStoreConfiguration.ConnectionString);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

解耦是 DI 的一个重要考虑因素,因此我不确定为什么 Microsoft 会引导用户将其类库耦合到外部依赖项(例如 )IOptions,无论它看起来多么微不足道或它应该提供什么好处。我还认为,某些好处IOptions似乎是过度设计的。例如,它允许我动态更改配置并跟踪更改 - 我已经使用了其他三个包含此功能的 DI 容器,但我从未使用过它一次...同时,我几乎可以向您保证团队会想要将 POCO 类或接口注入到库中以替换其设置ConfigurationManager,经验丰富的开发人员不会对无关的包装器接口感到高兴。我希望 ASP.NET Core 的未来版本中包含与我在此描述的类似的实用程序,或者有人为我提供令人信服的论据来说明为什么我错了。

  • `虽然使用 IOption 是做事的官方方式,但我似乎无法超越这样一个事实,即我们的外部库不需要了解有关 DI 容器的任何信息` &lt;-- 完全同意 - 抓狂试图消除对蹩脚的“IOptions”的依赖 (5认同)