​作为软件开发人员,我们当然喜欢一些可配置选项,尤其是当它允许我们改变应用程序的行为而无需修改或编译我们的应用程序时。无论你是使用新的还是旧的.NET时,可能希望利用json文件的配置。在这篇文章中,我们将探讨读取配置所需的必要步骤以及使用这些值。

.NET的配置历史

对于那些ASP.NET老兵,你可能还记得wen.config。虽然它没有完全被抛弃,但它在ASP.NET Core中扮演着不那么重要的角色。web.config是一个基于XML的文件,用于配置IIS的主机环境。在这个文件中,我们可以放置应用程序设置、加载额外的web模块、注册处理程序等等。

另一个限制是web.config更改后将迫使应用程序重新启动。更改可以很简单,如添加新的应用程序设置,也可以很复杂,如向请求管道添加新模块。ASP.NET应用程序必须重新加载,以确保逻辑一致性。开发人员可以将所有设置通过ConfigurationManager访问。坦率地说,随着时间的推移,开发人员将重新启动看作是一种功能,而不是一种障碍,使用它来重置陷入故障状态的应用程序。

现在的ASP.NET Core配置

ASP.NET Core看到了围绕配置产生主要问题,并试图通过以下三方面为开发人员改进:

  • 除了XML之外,还支持其他形式的配置格式。
  • 用依赖注入(DI)的友好方法替换ConfigurationManager。
  •  配置热更改,可以立即在访问的代码中发生。

这些变化反映了更先进的设置,并且对本地云的web应用程序更友好。应用程序配置可以来自多个位置,可扩展性使开发人员更容易避免自定义解决方案。

随着.NET采用async/await和异步编程,使用单例可能会导致死锁和性能开销。使DI支持配置后,给开发人员提供了更多的方式来使用设置依赖项,并将这些数据与任何源解耦。它还可以在不访问ConfigurationManager或web.config的情况下测试配置设置。

冷启动是所有用户的敌人,它会创造令人沮丧的体验。无需重新启动就可以进行更改的能力确保了更好的运行时体验。

看看具体代码

目前,我们已经讨论了配置的历史和当前状态,但是让我们跳到实际使用环节。

第一步是创建一个从配置提供程序读取设置的数据类。ASP.NET Core提供了多个开箱即用的功能,但最常用的是JSON配置提供程序。该类需要包含与JSON部分相同的结构。

public class HelloWorldOptions{
  public string Text { get; set; }
}

下一部分是讲述ASP.NET Core如何绑定HelloWorldOptions类。我们可以使用BindConfiguration方法做到这一点。

public void ConfigureServices(IServiceCollection services){
    services.AddOptions<HelloWorldOptions>()
            .BindConfiguration("HelloWorld");
}

字符串HelloWorld表示appsettings.json文件中的部分。

{
  "HelloWorld" : {
    "Text": "Hello, Khalid!"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

很好,现在我们准备好使用我们的配置信息了。接下来就有点让人困惑了。我们有三个接口可供选择:

  • IOptions<T>
  • IOptionsSnapshot<T>
  • IOptionsMonitor<T>

每个接口都包装了我们的配置数据,并为我们提供了略微不同的生命周期。

IOptions< T>被注册为一个单例,因此所有的值都被检索一次并存储在ASP.NET Core应用程序的内存。在应用程序启动后,这种方法无法读取任何更新的配置。注册为单例意味着ASP.NET可以将接口注入到任何依赖项中,而不必担心捕获它或导致内存泄漏问题。这个版本很可能是大多数人会使用的。

IOptionsSnapshot< T>具有作用域生存期。ASP.NET Core将对每个HTTP请求重新检查一次。

缓存每个请求的实例,直到用户收到响应。这个方法对于那些希望动态更改配置但仍然需要通过当前管道进行刷新的请求的人非常有用。这个版本有助于使用切换配置,而无需重新加载应用程序。

最后,IOptionsMonitor< T>与IOptionsSnapshot<T>很相似,却是单例生命周期。IOptionsMonitor方法对于应该立即处理的关键数据更改非常有用。长期存在的后台服务可能希望使用IOptionsMonitor继续接收配置更改,但不需要支付昂贵的对象创建成本。

既然我们知道了每个接口的优点,我们就可以使用我们的配置了。在启动时找到的Configure方法中,让我们添加一个新的GET终结点。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env){
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
​
    app.UseRouting();
​
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", async context =>
        {
            var options =
                context
                    .RequestServices
                    .GetRequiredService<IOptionsSnapshot<HelloWorldOptions>>()
                    .Value;

            await context.Response.WriteAsync(options.Text);
        });
    });
}

注意,在我们的代码中,我们使用的是IOptionsSnapshot。通过实例将允许我们更新配置,而不需要重新启动我们的应用程序。启动应用程序时,我们应该看到配置值。更改配置将更改请求的结果。

{
  "HelloWorld" : {
    "Text": "Hello, World!"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

重新加载后,我们现在看到的内容:

Hello, World!

这很有效,而且我们不需要为配置中的微小更改支付启动成本。

总结

最初使用ASP.NET Core中的配置可能会让人感到困惑。微软文档对IOption接口有一个详细的解释。大多数情况下,人们应该使用IOptions<T>,因为它可能是性能最好的。也就是说,如果我们想要热加载设置的能力,如果我们想要请求一致性,IOptionsSnapshot是最好的。

最后,如果你的应用严重依赖单例生存期,仍然需要热加载设置,考虑IOptionsMonitor。

原文链接:https://khalidabuhakmeh.com/aspnet-core-ioptions-configuration

聊聊ASP.NET Core中的配置-LMLPHP

02-20 15:09