.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注入所需依赖:
- 定义自定义事件处理类:
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); } }
- 在DI容器中注册该类:
builder.Services.AddTransient<CustomGitHubOAuthEvents>();
- 配置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
相关产品推荐
相关产品推荐

