| | |
| | | using Quartz.Spi; |
| | | using Quartz; |
| | | using System; |
| | | using System.Collections.Concurrent; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | | using System.Text; |
| | |
| | | namespace WIDESEAWCS_QuartzJob |
| | | { |
| | | /// <summary> |
| | | /// Job注入 |
| | | /// Job注入工厂,管理 Job 实例的 DI 作用域生命周期 |
| | | /// </summary> |
| | | public class JobFactory : IJobFactory |
| | | { |
| | |
| | | private readonly IServiceProvider _serviceProvider; |
| | | |
| | | /// <summary> |
| | | /// Job 实例与对应的 DI 作用域映射,用于在 ReturnJob 时正确释放资源 |
| | | /// </summary> |
| | | private readonly ConcurrentDictionary<IJob, IServiceScope> _scopes = new(); |
| | | |
| | | /// <summary> |
| | | /// Job注入 |
| | | /// </summary> |
| | | /// <param name="serviceProvider"></param> |
| | | /// <param name="serviceProvider">服务提供者</param> |
| | | public JobFactory(IServiceProvider serviceProvider) |
| | | { |
| | | _serviceProvider = serviceProvider; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 实现接口Job |
| | | /// 创建 Job 实例,并为每个 Job 创建独立的 DI 作用域 |
| | | /// </summary> |
| | | /// <param name="bundle"></param> |
| | | /// <param name="scheduler"></param> |
| | | /// <returns></returns> |
| | | /// <param name="bundle">触发器触发上下文</param> |
| | | /// <param name="scheduler">调度器实例</param> |
| | | /// <returns>Job 实例</returns> |
| | | public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) |
| | | { |
| | | try |
| | | { |
| | | if (App.ExpDateTime != null && (DateTime.Now - App.ExpDateTime.GetValueOrDefault()).Seconds > 0) |
| | | // 验证有效期,使用 TotalSeconds 避免 .Seconds 取模导致间歇性判断错误 |
| | | if (App.ExpDateTime != null && (DateTime.Now - App.ExpDateTime.GetValueOrDefault()).TotalSeconds > 0) |
| | | { |
| | | throw new InvalidOperationException($"验证错误"); |
| | | } |
| | | |
| | | // 为每个 Job 创建独立的 DI 作用域,确保 Scoped 服务正确释放 |
| | | IServiceScope serviceScope = _serviceProvider.CreateScope(); |
| | | IJob? job = serviceScope.ServiceProvider.GetService(bundle.JobDetail.JobType) as IJob; |
| | | |
| | | if (job == null) |
| | | { |
| | | // Job 解析失败时立即释放作用域,避免泄漏 |
| | | serviceScope.Dispose(); |
| | | throw new InvalidOperationException($"无法解析 Job 类型: {bundle.JobDetail.JobType.Name}"); |
| | | } |
| | | |
| | | // 保存 scope 引用,以便 ReturnJob 时释放 |
| | | _scopes[job] = serviceScope; |
| | | return job; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.Out.WriteLine(ex.ToString()); |
| | | throw new Exception(ex.Message); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// Job注入 |
| | | /// 释放 Job 实例及其关联的 DI 作用域 |
| | | /// </summary> |
| | | /// <param name="job"></param> |
| | | /// <param name="job">待释放的 Job 实例</param> |
| | | public void ReturnJob(IJob job) |
| | | { |
| | | IDisposable? disposable = job as IDisposable; |
| | | disposable?.Dispose(); |
| | | // 先释放关联的 DI 作用域(包括其中所有 Scoped 服务) |
| | | if (_scopes.TryRemove(job, out IServiceScope? scope)) |
| | | { |
| | | scope.Dispose(); |
| | | } |
| | | |
| | | // 再释放 Job 本身 |
| | | (job as IDisposable)?.Dispose(); |
| | | } |
| | | } |
| | | } |