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

.NET 7中OAuth OnCreatingTicket事件如何正确使用DI服务?

在.NET 7 WebApplication的OAuth事件中正确处理数据库操作的方案

我在基于.NET 7的WebApplication中使用GitHub OAuth扩展。AuthenticationBuilder是在应用构建、DI服务提供程序(Service Provider)创建之前的阶段配置的。但我需要在OAuth.OnCreatingTicket事件中使用UnitOfWork/仓储等DI依赖链。之前我在事件的匿名方法里提前实例化了一个临时容器副本,这样做会触发警告,提示正在构建第二个容器,显然违背了单例设计,不是合理方案。

请问有什么方法可以在OAuth.OnCreatingTicket事件中正确处理数据库操作?难道只能传递配置并手动实例化所有类吗?

现有代码示例:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddTransient<IUnitOfWork,UnitOfWork>();
builder.Services.AddSingleton<IDocumentStore>(s => 
    DocumentStoreFactory.CreateStore(
        builder.Configuration["Server"], 
        builder.Configuration["Database"]));

builder.Services.AddAuthentication(...)
    .AddCookie(...)
    .AddGitHub(o =>
    {
        o.ClientId = builder.Configuration["GitHub:ClientID"]!;
        o.ClientSecret = builder.Configuration["GitHub:ClientSecret"]!;
        o.Events = new OAuthEvents
        {
            OnCreatingTicket = async context =>
            {
                // 构建临时服务提供程序以满足UnitOfWork/Service/DocumentStore的DI依赖链
                var sp = builder.Services.BuildServiceProvider();
                await OauthPersistUser.HandleGitHubOauthTicket(context, sp.GetService(typeof(ILogger<>)), sp.GetService<IUnitOfWork>());
            }
        };
    });

var app = builder.Build();
// 应用服务提供程序容器已构建完成

解决方案:利用HttpContext.RequestServices获取服务

你不需要提前构建临时服务提供程序,OAuthCreatingTicketContext本身就可以通过context.HttpContext.RequestServices访问到已经初始化完成的DI容器。这是因为OnCreatingTicket事件是在请求处理流程中触发的,此时应用的服务提供程序已经完全构建完毕。

修改后的代码如下:

builder.Services.AddAuthentication(...)
    .AddCookie(...)
    .AddGitHub(o =>
    {
        o.ClientId = builder.Configuration["GitHub:ClientID"]!;
        o.ClientSecret = builder.Configuration["GitHub:ClientSecret"]!;
        o.Events = new OAuthEvents
        {
            OnCreatingTicket = async context =>
            {
                // 从HttpContext获取已初始化的服务提供程序
                var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OauthPersistUser>>();
                var unitOfWork = context.HttpContext.RequestServices.GetRequiredService<IUnitOfWork>();
                
                await OauthPersistUser.HandleGitHubOauthTicket(context, logger, unitOfWork);
            }
        };
    });

关键说明:

  • HttpContext.RequestServices是请求范围内的服务提供程序,能正确解析所有已注册的服务,包括单例、作用域和瞬态类型,完全符合DI设计原则。
  • 使用GetRequiredService<T>替代GetService<T>,如果服务未注册会直接抛出异常,便于调试和发现配置问题。
  • 避免了构建临时容器带来的资源浪费和单例实例不一致的问题。

备选方案:注册自定义OAuthEvents实例

如果需要更清晰的代码结构,可以将OAuthEvents注册为服务,通过DI注入所需依赖:

  1. 定义自定义事件处理类:
public class CustomGitHubOAuthEvents : OAuthEvents
{
    private readonly ILogger<CustomGitHubOAuthEvents> _logger;
    private readonly IUnitOfWork _unitOfWork;

    public CustomGitHubOAuthEvents(ILogger<CustomGitHubOAuthEvents> logger, IUnitOfWork unitOfWork)
    {
        _logger = logger;
        _unitOfWork = unitOfWork;
        
        OnCreatingTicket = HandleCreatingTicket;
    }

    private async Task HandleCreatingTicket(OAuthCreatingTicketContext context)
    {
        await OauthPersistUser.HandleGitHubOauthTicket(context, _logger, _unitOfWork);
    }
}
  1. 在DI容器中注册该类:
builder.Services.AddTransient<CustomGitHubOAuthEvents>();
  1. 配置GitHub OAuth时使用该实例:
builder.Services.AddAuthentication(...)
    .AddCookie(...)
    .AddGitHub(o =>
    {
        o.ClientId = builder.Configuration["GitHub:ClientID"]!;
        o.ClientSecret = builder.Configuration["GitHub:ClientSecret"]!;
        o.Events = sp => sp.GetRequiredService<CustomGitHubOAuthEvents>();
    });

这种方式将事件处理逻辑与配置代码分离,更符合SOLID原则,也便于单元测试。


内容的提问来源于stack exchange,提问作者G. Scott Singleton

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 14:45:33