C# ASP.NET异步方法依赖注入对象已释放异常问题求助
问题现象
在C# ASP.NET服务中调用依赖项时触发以下错误:
CG10.Bot.DiscordSender[0]
System.ObjectDisposedException: 无法访问已释放的对象。
对象名: 'IServiceProvider'。
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ThrowHelper.ThrowObjectDisposedException()
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
at CG10.Bot.DiscordSender.SlashCommandHandler(SocketSlashCommand command) in E:\SERVER\CG10Website\Bot\DiscordSender.cs:line 261
自定义的非异步Initialize方法中依赖项可正常工作,但所有由Discord.Net包事件处理器触发的异步方法均无法运行,涉及SQL数据库或尝试使用IServiceManager修复的场景。
相关代码
服务注册(Program文件)
builder.Services.AddScoped<IDiscordSender, DiscordSender>();
构造函数注入代码
public DiscordSender( UserManager<IdentityUser> userManager, RoleManager<IdentityRole> roleManager, IUnitOfWork db, TFDbContext tfdb, ILogger<DiscordSender> logger, ILogger<SlashCommand> loggerOther, IMemoryCache memoryCache, IServiceProvider service) { _roleManager = roleManager; _userManager = userManager; _db = db; _tfdb = tfdb; _logger = logger; _loggerOther = loggerOther; _memoryCache = memoryCache; _service = service; }
出错的异步方法代码
public async Task SlashCommandHandler(SocketSlashCommand command) { try { _db = _service.GetService<IUnitOfWork>(); //此处崩溃,进入捕获块 if (_db == null) { _logger.LogCritical("Failed to receive IUnitOfWork service"); return; } //后续代码已省略
已尝试将异步方法返回值从Void改为Task,但问题仍未解决。
解决方案
核心原因
DiscordSender是Scoped生命周期服务,其构造函数注入的IServiceProvider实际绑定到当前请求的服务作用域。当Discord.Net的事件触发时,原请求作用域已经被释放,导致IServiceProvider无法使用。
修复步骤
注入
IServiceScopeFactory替代直接注入IServiceProvider
修改构造函数,替换依赖注入的服务类型:private readonly IServiceScopeFactory _serviceScopeFactory; public DiscordSender( UserManager<IdentityUser> userManager, RoleManager<IdentityRole> roleManager, IUnitOfWork db, TFDbContext tfdb, ILogger<DiscordSender> logger, ILogger<SlashCommand> loggerOther, IMemoryCache memoryCache, IServiceScopeFactory serviceScopeFactory) { _roleManager = roleManager; _userManager = userManager; _db = db; _tfdb = tfdb; _logger = logger; _loggerOther = loggerOther; _memoryCache = memoryCache; _serviceScopeFactory = serviceScopeFactory; }在异步方法中创建独立作用域获取服务
在事件触发的异步方法内,创建新的服务作用域,从该作用域中获取所需服务,并通过using语句确保作用域自动释放:public async Task SlashCommandHandler(SocketSlashCommand command) { try { using var scope = _serviceScopeFactory.CreateScope(); var db = scope.ServiceProvider.GetRequiredService<IUnitOfWork>(); // 使用db执行数据库操作及其他业务逻辑 // 后续代码... } catch (Exception ex) { _logger.LogError(ex, "处理Slash命令时出错"); } }移除对全局
_db字段的复用
不要复用构造函数中注入的IUnitOfWork实例(它属于原请求作用域,会随作用域释放而失效),每次事件触发时都从新作用域获取独立的服务实例。
额外说明
- Scoped服务的生命周期与请求上下文绑定,而Discord.Net的事件在请求上下文之外触发,必须创建独立作用域来管理服务的生命周期。
- 使用
using语句可以确保服务作用域在使用完成后自动释放,避免资源泄漏。
内容的提问来源于stack exchange,提问作者cadergator10

