Quartz.NET 3.0 seems to launch all jobs in the same scope

后端 未结 1 782
礼貌的吻别
礼貌的吻别 2021-01-21 14:34

I have a hard time using Quartz 3.0.7 with ASP.NET Core 2.2 after I have defined two jobs that rely on a scoped service (ScopedDataAccess) that is a wrapper upon my database con

相关标签:
1条回答
  • 2021-01-21 15:06

    As i know, this is not possible with Quartz, i struggled with the same issues and the only solution i found was to use a ServiceLocator and create the scope explicitly in the Job.

    I ended with something like that:

    // 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();
            }
        }
    }
    

    In this case, your worker is still scoped but the job isn't anymore. So you can still use your Worker in other places in your solution and the scope still works. You need to implement the ServiceLocator by yourself depending on the DI do you use and IServiceLocator must also be defined by you.

    Edit

    In one of our projects we use this:

    /// <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;
    }
    

    We use mostly SimpleInjector with this implementation:

    /// <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>();
        }
    }
    

    As you can see, this is just a simple wrapper but helps to hide the real DI Framework from the consumers. I hope this helps a little bit to understand your needed implementation.

    0 讨论(0)
提交回复
热议问题