You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET 5中如何在Startup.cs将Scoped依赖注入到MS Identity Platform的OnTicketReceived事件中

解决ASP.NET 5中OpenIdConnect事件里注入Scoped服务的问题

我完全理解你遇到的困境——在配置MS Identity Platform的事件时,想使用Scoped服务却被生命周期冲突卡住了。咱们一步步拆解问题,找到可行的解决方案:

为什么之前的方法会报错?

首先得明确ASP.NET Core的服务生命周期规则:

  • Singleton:应用启动时创建,直到应用关闭才销毁,全局唯一。
  • Scoped:每个请求创建一个新实例,请求结束后销毁。
  • Transient:每次获取都会创建新实例,用完即销毁。

你把AzureAdOpendIdHandler注册为Singleton,却在它的构造函数里注入Scoped的AuthenticationManager——这就违反了生命周期规则:Singleton服务会一直存活,而Scoped服务本应该随请求销毁,注入后它会被"强制升级"为Singleton,导致所有请求复用同一个实例,这会引发线程安全和数据污染问题,所以框架直接抛出错误阻止这种情况。

至于为什么Singleton/Transient可以注入到Singleton的配置类里:

  • Singleton服务和配置类生命周期一致,没有冲突;
  • Transient服务每次获取都是新实例,即使配置类是Singleton,每次使用Transient服务都是独立的,不会有复用问题(但如果Transient依赖Scoped服务,还是会报错)。

正确的解决方案:在事件处理中动态获取Scoped服务

核心思路是:不在配置类的构造函数里注入Scoped服务,而是在OnTicketReceived事件触发时(每个请求上下文里),从当前请求的服务容器中获取Scoped实例。

方案1:直接在ConfigureServices中配置事件

保留你的Scoped服务注册,然后在配置OpenIdConnectOptions时,从事件上下文的HttpContext.RequestServices获取服务:

// 注册Scoped服务
services.AddScoped<IAuthenticationManager, AuthenticationManager>();

// 配置OpenIdConnect选项
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    options.Events = new OpenIdConnectEvents
    {
        OnTicketReceived = async context =>
        {
            // 从当前请求的服务容器中获取Scoped的AuthenticationManager
            var authManager = context.HttpContext.RequestServices.GetRequiredService<IAuthenticationManager>();
            
            // 在这里执行你的自定义逻辑
            await authManager.HandlePostSignIn(context);
            
            await Task.CompletedTask;
        }
    };
});

方案2:使用IConfigureNamedOptions封装(更整洁的方式)

如果你还是想用IConfigureNamedOptions来封装配置逻辑,同样不要在构造函数注入Scoped服务,而是在事件处理中动态获取:

public class AzureAdOpenIdHandler : IConfigureNamedOptions<OpenIdConnectOptions>
{
    public void Configure(string name, OpenIdConnectOptions options)
    {
        // 只处理指定的AuthenticationScheme
        if (name != OpenIdConnectDefaults.AuthenticationScheme)
            return;

        options.Events = new OpenIdConnectEvents
        {
            OnTicketReceived = async context =>
            {
                // 从当前请求上下文获取Scoped服务
                var authManager = context.HttpContext.RequestServices.GetRequiredService<IAuthenticationManager>();
                await authManager.HandlePostSignIn(context);
            }
        };
    }

    // 实现IConfigureOptions的默认方法
    public void Configure(OpenIdConnectOptions options)
    {
        Configure(OpenIdConnectDefaults.AuthenticationScheme, options);
    }
}

然后在Startup.cs中注册这个Handler为Singleton(没问题,因为它不再依赖Scoped服务):

services.AddScoped<IAuthenticationManager, AuthenticationManager>();
services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, AzureAdOpenIdHandler>();

为什么这个方法可行?

HttpContext.RequestServices是当前请求的服务容器,它会为每个请求创建Scoped服务的实例,完全符合Scoped的生命周期要求。在OnTicketReceived事件触发时,请求上下文已经存在,所以我们可以安全地从这里获取Scoped服务,不会有生命周期冲突的问题。

这样你就能同时满足三个需求:扩展MS Identity Platform、使用DI获取自定义类、注入Scoped服务。

内容的提问来源于stack exchange,提问作者user3280560

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 11:02:42