首页 > 解决方案 > 在 Startup.ConfigureServices 方法期间访问依赖注入对象

问题描述

我有一个 ASP.NET Core 3 应用程序,并且正在使用 AzureAD 进行身份验证。我的 Startup.ConfigureSerivces 方法中有以下几行,其目的是在附加 cookie 时执行一些业务规则。

services.Configure<CookiePolicyOptions>(options => {
    options.CheckConsentNeeded = ctx => true;
    options.MinimumSameSitePolicy = SameSiteMode.None;
    options.OnAppendCookie = ctx => {
        var svc = ctx.Context.RequestServices.GetRequiredService<IUserInformationService>();
        // do something with svc        
    };
});
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
    .AddAzureAD(options => {
        Configuration.Bind("AzureAd", options);
    });

这很好用,我得到了方法IUserInformationService中预期的注入对象,OnAppendCookieAzureAD 信息取自 appsettings.json。

然而,最近,有关 AzureAD 租户的信息不能驻留在 appsettings.json 中,而我现在必须查阅数据库。我有一个已经查询数据库并获取 AD 设置的服务。就像是:

public interface IAzureADConfiguration {
    void Configure(AzureADOptions options);
}

但是,在调用AddAzureAD. 我想要的是这样的:

services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
    .AddAzureAD(options => {
        var svc = ???
        svc.Configure(options);
        Configuration.Bind("AzureAd", options);
    });

因为我无法像HttpContext在. 有没有办法在这个阶段获得注入的对象?我不想对实例进行硬编码,因为此要求将来可能会发生变化(即添加另一种方法来配置我获取 Azure 广告设置的位置)。提前致谢!AddAzureADOnAppendCookie

标签: c#asp.net-coredependency-injection

解决方案


选项 1:配置回调

感谢@Nkosi 指出相关文档:使用 DI 服务配置选项

他们的回答让我开始走上正轨,但提供的代码示例对我不起作用,因为它没有考虑命名选项(AzureAD 配置使用的选项)。在弄清楚这一点并简化一点之后,我最终得到了以下内容......

对于AzureADOptions,最好的选择似乎是在提供给AddAzureAD. 两个回调将传递相同的AzureADOptions实例,因此现有回调可以保持原样加载“基本”值,并且配置后回调可以根据需要覆盖它们。

// Configure Azure AD to load settings from the app config.
// This is unchanged (apart from syntax) from the original question.
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
    .AddAzureAD(options => Configuration.Bind("AzureAd", options));

// Add a post-configure callback to augment the app config-provided values
// with service-provided values.
services.AddOptions<AzureADOptions>(AzureADDefaults.AuthenticationScheme)
    .PostConfigure<IAzureADConfiguration>((options, service) =>
        service.Configure(options));

如果您不想从应用配置中加载基值,只需替换Configuration.Bind("AzureAD", options)为空块 ( { })。

AddAzureAD使用命名选项实例,因此请确保将相同的名称传递给PostConfigureAddAuthentication。如果两个地方没有使用相同的名称,则配置后回调将不会配置正确的选项实例(或根本不会运行)。

注意:如果有人想使用这个答案来配置 Azure AD 以外的东西......当没有注册现有的选项实例或回调时,您应该使用Configure方法而不是PostConfigure,并且您可能不需要提供名称。

services.AddOptions<MyOptionsType>()
    .Configure<IMyConfigurationService>((options, service) =>
        service.Configure(options));

选项 2:自定义配置提供程序

虽然这不是您问题的直接答案,但它可能是激发您提出问题的问题的可行解决方案。

您可以实现自定义配置提供程序来从数据库加载设置,而不是编写配置服务。然后,您应该可以AddAzureAD使用Configuration.Bind.

文档中的示例new创建了一个 EF 数据上下文,而不是由 DI 容器提供。这违背了 DI 的本能,但我认为在这种情况下很好。示例提供程序是一个小型且包含完善的子系统。它的数据库配置仍然是注入的(尽管不是通过 DI 容器),并且它仍然可以通过注入 EF 内存数据库的配置来进行单元测试。它没有任何其他依赖项可以换出。DI 容器不会增加太多价值——系统已经拥有它需要的所有依赖注入。


推荐阅读