使用AddSingleton注册依赖AuthenticationStateProvider的服务时触发异常
问题
我有一个依赖AuthenticationStateProvider的服务。当使用AddScoped注册该服务时运行正常,但改用AddSingleton注册后,在服务调用AuthenticationStateProvider的GetAuthenticationStateAsync方法时会抛出以下异常:
GetAuthenticationStateAsync was called before SetAuthenticationState.
我理解作用域的差异,但不清楚为何这会对AuthenticationStateProvider产生此类影响。
我的服务代码:
public class MyService : IMyService { AuthenticationStateProvider _provider; public MyService(AuthenticationStateProvider provider) { _provider = provider; } public async Task<MyObject> DoThingsAsyc() { //Exception happens here var user = (await _provider.GetAuthenticationStateAsync()).User; ... } }
Program.cs代码:
WebApplicationBuilder builder = WebApplication.CreateBuilder(args); builder.Services.AddSingleton<IMyService, MyService>();
原因与解决方案
- 核心原因:
AuthenticationStateProvider本身是Scoped服务(Blazor默认注册方式),它的生命周期和当前请求/组件作用域绑定。当你把MyService注册为Singleton时,它会在应用启动时就被创建,此时注入的AuthenticationStateProvider属于启动时的根作用域,而不是后续每个用户会话的有效作用域,根本没机会被Blazor初始化(调用SetAuthenticationState),所以调用GetAuthenticationStateAsync会抛出异常。 - Scoped服务正常的原因:当
MyService是Scoped时,它会和每个请求/组件作用域一同创建,此时注入的AuthenticationStateProvider已经在当前作用域中被初始化完成,自然能正常调用GetAuthenticationStateAsync。 - 解决方法:
- 优先保持
MyService为Scoped注册,这是最贴合Blazor身份验证模型的方案,从根源避免生命周期不匹配问题。 - 如果必须使用Singleton,不要直接注入
AuthenticationStateProvider,改为注入IServiceProvider,在需要调用时从当前有效作用域获取实例:
这样每次调用方法时,都会从当前有效的用户/请求作用域获取已初始化的public class MyService : IMyService { private readonly IServiceProvider _serviceProvider; public MyService(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public async Task<MyObject> DoThingsAsyc() { using var scope = _serviceProvider.CreateScope(); var provider = scope.ServiceProvider.GetRequiredService<AuthenticationStateProvider>(); var user = (await provider.GetAuthenticationStateAsync()).User; ... } }AuthenticationStateProvider实例。
- 优先保持
内容的提问来源于stack exchange,提问作者Jeremy
相关产品推荐
相关产品推荐

