代码之家  ›  专栏  ›  技术社区  ›  Alexei - check Codidact

石英.NET3.0似乎在同一范围内启动所有作业

  •  0
  • Alexei - check Codidact  · 技术社区  · 6 年前

    services.AddScoped<IScopedDataAccess, ScopedDataAccess>();
    
    services.AddDbContext<AggregatorContext>(opt => opt.UseSqlServer(configuration.GetConnectionString("Default")));
    

    问题是,两个作业都接收作用域服务的相同实例(因此也是相同的数据库上下文),因此由于并行使用而导致上下文崩溃。

    启动.cs

    作业被定义为“作用域”,我的期望是每个实例在自己的“作用域”中运行

    private void ConfigureQuartz(IServiceCollection services, params Type[] jobs)
    {
        services.AddSingleton<IJobFactory, QuartzJobFactory>();
        services.Add(jobs.Select(jobType => new ServiceDescriptor(jobType, jobType, ServiceLifetime.Scoped)));
    
        services.AddSingleton(provider =>
        {
            var schedulerFactory = new StdSchedulerFactory();
            var scheduler = schedulerFactory.GetScheduler().Result;
    
            scheduler.JobFactory = provider.GetService<IJobFactory>();
            scheduler.Start();
            return scheduler;
        });
    }
    
    protected void StartJobs(IApplicationBuilder app, IApplicationLifetime lifetime)
    {
        var scheduler = app.ApplicationServices.GetService<IScheduler>();
        var configService = app.ApplicationServices.GetService<IConfigurationService>();
    
        QuartzServicesUtilities.StartJob<ArticleXUserDataRefresherJob>(scheduler, 
            TimeSpan.FromSeconds(configService.ArticleXUserDataRefresherJobPeriod));
        QuartzServicesUtilities.StartJob<LinkDataFetchJob>(scheduler,
            TimeSpan.FromSeconds(configService.LinkDataJobPeriod));
    
        lifetime.ApplicationStarted.Register(() => scheduler.Start());
        lifetime.ApplicationStopping.Register(() => scheduler.Shutdown());
    }
    

    public class QuartzServicesUtilities
    {
        public static void StartJob<TJob>(IScheduler scheduler, TimeSpan runInterval)
            where TJob : IJob
        {
            var jobName = typeof(TJob).FullName;
    
            var job = JobBuilder.Create<TJob>()
                .WithIdentity(jobName)
                .Build();
    
            var trigger = TriggerBuilder.Create()
                .WithIdentity($"{jobName}.trigger")
                .StartNow()
                .WithSimpleSchedule(scheduleBuilder =>
                    scheduleBuilder
                        .WithInterval(runInterval)
                        .RepeatForever())
                .Build();
    
            scheduler.ScheduleJob(job, trigger);
        }
    }
    

    夸茨工厂

    public class QuartzJobFactory : IJobFactory
    {
        private readonly IServiceProvider _serviceProvider;
    
        public QuartzJobFactory(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
    
        public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
        {
            var jobDetail = bundle.JobDetail;
    
            var job = (IJob)_serviceProvider.GetService(jobDetail.JobType);
            return job;
        }
    
        public void ReturnJob(IJob job) { }
    }
    

    0 回复  |  直到 6 年前
        1
  •  1
  •   Rabban    6 年前

    据我所知,这在Quartz中是不可能的,我也遇到了同样的问题,我找到的唯一解决方案是使用ServiceLocator并在作业中显式创建作用域。

    // Pseudo-Code
    public class MyJob : IJob
    {
        private readonly IServiceLocator _serviceLocator;
    
        public MyJob(IServiceLocator serviceLocator)
        {
            _serviceLocator = serviceLocator;
        }
    
        public async Task Execute(JobExecutionContext context)
        {
            using(_serviceLocator.BeginScope())
            {
                var worker = _serviceLocator.GetService<MyWorker>();
                await worker.DoWorkAsync();
            }
        }
    }
    

    在本例中,您的工作人员仍处于范围内,但该作业已不再存在。因此,您仍然可以在解决方案的其他地方使用Worker,而且范围仍然有效。 IServiceLocator 也必须由您定义。

    在我们的一个项目中,我们使用:

    /// <summary>
    /// A simple service locator to hide the real IOC Container.
    /// Lowers the anti-pattern of service locators a bit.
    /// </summary>
    public interface IServiceLocator
    {
        /// <summary>
        /// Begins an new async scope.
        /// The scope should be disposed explicitly.
        /// </summary>
        /// <returns></returns>
    
        IDisposable BeginAsyncScope();
        /// <summary>
        /// Gets an instance of the given <typeparamref name="TService" />.
        /// </summary>
        /// <typeparam name="TService">Type of the requested service.</typeparam>
        /// <returns>The requested service instance.</returns>
        TService GetInstance<TService>() where TService : class;
    }
    

    /// <summary>
    /// SimpleInjector implementation of the service locator.
    /// </summary>
    public class ServiceLocator : IServiceLocator
    {
        #region member vars
    
        /// <summary>
        /// The SimpleInjector container.
        /// </summary>
        private readonly Container _container;
    
        #endregion
    
        #region constructors and destructors
    
        public ServiceLocator(Container container)
        {
            _container = container;
        }
    
        #endregion
    
        #region explicit interfaces
    
        /// <inheritdoc />
        public IDisposable BeginAsyncScope()
        {
            return AsyncScopedLifestyle.BeginScope(_container);
        }
    
        /// <inheritdoc />
        public TService GetInstance<TService>()
            where TService : class
        {
            return _container.GetInstance<TService>();
        }
    }
    

    如您所见,这只是一个简单的包装器,但有助于向消费者隐藏真正的DI框架。 我希望这有助于理解您需要的实现。

    推荐文章