如何在 ASP.NET Core 5 运行时动态重新配置服务?

Láz*_*olt 6 c# entity-framework dependency-injection asp.net-core

我在我的网络应用程序中使用 Google 身份验证,并且 OAuth 密钥当前硬编码在ConfigureServices 中:

services.AddAuthentication()
    .AddGoogle(options =>
    {
        options.ClientId = "my-client-id";
        options.ClientSecret = "my-client-secret";
    });
Run Code Online (Sandbox Code Playgroud)

但是,我想让站点管理员有机会从 Web 应用程序的设置页面更改 ClientId 和 ClientSecret,最好无需重新启动服务器。

为此,当用户在设置页面上点击“保存”时,我必须以某种方式触发 Google 服务和 GoogleOptions 对象的重新配置。这就是我遇到的麻烦。另外,我想将这些设置存储在 EF Core DbContext 中,而不是存储在物理配置文件中。

到目前为止,我已尝试将设置移动到实现 IPostConfigureOptions 的单独类中。这应该允许我注入我的数据库上下文,因为根据文档,PostConfigure 应该在所有其他配置发生后运行。设置已从这个新类正确加载,但数据库上下文的注入失败,并出现以下异常:

System.InvalidOperationException: Cannot consume scoped service 'AppDatabase' from singleton 'IOptionsMonitor`1[GoogleOptions]'
Run Code Online (Sandbox Code Playgroud)

这很奇怪,因为ConfigureGoogleOptions 注册为Scoped,而不是Singleton。

这是我的选项类:

public class ConfigureGoogleOptions : IPostConfigureOptions<GoogleOptions>
{
    private readonly AppDatabase database;

    public ConfigureGoogleOptions(AppDatabase database)
    {
        this.database = database;
    }
    
    public void PostConfigure(string name, GoogleOptions options)
    {
        options.ClientId = "my-client-id.apps.googleusercontent.com";
        options.ClientSecret = "my-client-secret";
    }
}
Run Code Online (Sandbox Code Playgroud)

并在ConfigureServices中注册它:

services.AddScoped<IPostConfigureOptions<GoogleOptions>, ConfigureGoogleOptions>();
Run Code Online (Sandbox Code Playgroud)

即使数据库注入有效,仍然存在第二个问题。我的类中的函数PostConfigure仅在应用程序启动后被调用一次,再也不会调用。我假设它将设置缓存在某处,并且我不知道如何使该缓存无效或禁用,以便我可以动态提供值。

简短摘要/tl;dr:

我想从我自己的数据库加载 Google OAuth 服务的 ClientId 和 ClientSecret 设置,并且希望能够在服务器运行时动态更改它们。

Kin*_*ing 5

在内部,谷歌处理程序将用于IOptionsMonitor<GoogleOptions>获取GoogleOptions 一次或直到重新加载为止(例如,当从配置文件绑定选项并保存文件时将触发重新加载)。IOptionsMonitor内部将使用该IOptionsMonitorCache缓存并将其注册为单例。因此,您从中获取的选项实例与处理程序内用于各种操作的选项IOptionsMonitor<GoogleOptions>实例相同(参考) 。AuthenticationHandler<GoogleOptions>.Options即使其他代码如果使用该选项也应该正确地从IOptionsMonitor<GoogleOptions>.

因此,要在运行时更改选项,就像这样简单:

//inject the IOptionsMonitor<GoogleOptions> into _googleOptionsMonitor;
var runtimeOptions = _googleOptionsMonitor.Get(GoogleDefaults.AuthenticationScheme);
//you change properties of runtimeOptions here
//...
Run Code Online (Sandbox Code Playgroud)

这里重要的一点是我们需要使用GoogleDefaults.AuthenticationSchemeas 来获取正确的选项实例。将IOptionsMonitor.CurrentValue使用默认键Options.DefaultName(这是一个空字符串)。