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

.NET7中Quartz注入的IServiceProvider始终已释放问题求助

问题原因分析

Quartz Job与BackgroundService的生命周期差异是核心问题:

  • BackgroundService作为托管服务是单例生命周期,整个应用运行期间实例始终存在,注入的IServiceProvider是根容器实例,不会被释放。
  • 你的Quartz Job默认是**瞬时(Transient)**生命周期:当Execute方法执行完成后,Quartz会判定Job执行结束,立即释放Job实例及其依赖的Scoped服务(包括构造函数注入的IServiceProvider——它实际是Job对应的Scoped容器)。但你在Execute里启动的EventProcessorClient会在后台长期运行,当它触发OnProcessEvent回调时,Job实例已被回收,对应的IServiceProvider也已被释放,因此抛出对象已释放异常。

修复方案

方案1:将Job设置为单例生命周期

在注册Quartz Job时指定单例生命周期,确保Job实例不会被释放,注入的IServiceProvider为根容器实例,始终可用:

services.AddQuartz(configurator =>
{
    configurator.UseMicrosoftDependencyInjectionJobFactory();

    const string eventHubJobKey = nameof(EventHubLogProcessorJob);

    configurator.AddJob<EventHubLogProcessorJob>(options => 
        options.WithIdentity(eventHubJobKey)
               .WithSingletonLifetime()); // 新增单例生命周期配置
    
    configurator.AddTrigger(options => options.ForJob(eventHubJobKey).StartNow());
});

方案2:注入IServiceScopeFactory替代IServiceProvider

IServiceScopeFactory是单例服务,不会被释放,用它创建Scope更可靠:

  1. 修改Job构造函数的依赖注入:
private readonly ILogger<EventHubLogProcessorJob> _logger;
private readonly IServiceScopeFactory _serviceScopeFactory;

public EventHubLogProcessorJob(ILogger<EventHubLogProcessorJob> logger,
    IServiceScopeFactory serviceScopeFactory)
{
    Guard.Against.Null(logger);
    Guard.Against.Null(serviceScopeFactory);

    _logger = logger;
    _serviceScopeFactory = serviceScopeFactory;
}
  1. 修改InsertDataIntoDatabaseAsync方法:
private async Task InsertDataIntoDatabaseAsync()
{
    using var scope = _serviceScopeFactory.CreateScope();
    var repository = scope.ServiceProvider.GetRequiredService<IRepository>();
    
    _logger.LogInformation("Count: {Count}", _clientEvents.Count.ToString());

    foreach (var clientEvent in _clientEvents)
    {
        var activity = clientEvent.ToEntity();
        repository.Insert(activity);
        await repository.SaveChangesAsync();
    }
}

额外优化建议

你的EventProcessorClient在Execute方法启动后,没有监听取消信号,会导致Execute方法提前结束(即使处理器仍在运行)。建议添加取消逻辑,确保Job生命周期与处理器同步:

public async Task Execute(IJobExecutionContext context)
{
    var cts = CancellationTokenSource.CreateLinkedTokenSource(context.CancellationToken);
    var storageClient = new BlobContainerClient(StorageConnectionString, BlobContainerName);
    var processor = new EventProcessorClient(
        storageClient, ConsumerGroup, EventHubConnectionString, EventProcessorClientOptions);

    try
    {
        processor.ProcessEventAsync += OnProcessEvent;
        processor.ProcessErrorAsync += OnProcessError;
        
        await processor.StartProcessingAsync(cts.Token);
        // 等待取消信号,避免Execute方法提前结束
        await Task.Delay(Timeout.Infinite, cts.Token);
    }
    catch (TaskCanceledException e)
    {
        _logger.LogError(e.Demystify(), "Task was canceled");
    }
    catch (Exception e)
    {
        _logger.LogError(e.Demystify(), "An unhandled exception has occurred");
    }
    finally
    {
        // 确保停止处理器并解绑事件
        await processor.StopProcessingAsync();
        processor.ProcessEventAsync -= OnProcessEvent;
        processor.ProcessErrorAsync -= OnProcessError;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 18:31:45