【问题标题】:FluentScheduler dependency injection in constructor构造函数中的 FluentScheduler 依赖注入
【发布时间】:2018-01-17 11:59:05
【问题描述】:

我正在尝试使用 FluentScheduler 处理 ASP.net Core API 中的一些后台任务。

该作业应根据几个标准在特定时间间隔每天发送推送通知。我浏览了文档并实现了一个测试功能,以在控制台窗口中打印一些输出。它按预期的时间间隔工作。

但我要做的实际工作涉及数据库上下文,它提供必要的信息来执行发送通知的标准。

我的问题是我无法在 MyJob 类中使用带参数的构造函数,这会引发缺少方法异常

PS: 根据 Scott Hanselman 的这篇文章,FluentScheduler 似乎很有名,但我无法从在线社区获得任何帮助。但显然,它很容易掌握。

public class MyJob : IJob
{
    private ApplicationDbContext _context;

    public MyJob(ApplicationDbContext context)
    {
        _context = context;
    }

    public void Execute()
    {
        Console.WriteLine("Executed");
        SendNotificationAsync();
    }

    private async Task SendNotificationAsync()
    {
        var overdues = _context.Borrow.Join(
            _context.ApplicationUser,
            b => b.ApplicationUserId,
            a => a.Id,
            (a, b) => new { a, b })
            .Where(z => (z.a.ReturnedDate == null) && (z.a.BorrowApproval == 1))
            .Where(z => z.a.ReturnDate.Date == new DateTime().Date.AddDays(1).Date)
            .Select(z => new { z.a.ApplicationUserId, z.a.Book.ShortTitle, z.a.BorrowedDate, z.b.Token })
            .ToList();

        Console.WriteLine("Acknowledged");

        foreach (var r in overdues)
        {
            string message = "You are running late! The book '" + r.ShortTitle + "' borrowed on '" + r.BorrowedDate + "' due tomorrow.";
            Console.WriteLine(message);
            await new PushNotificationService().sendAsync(r.Token, "Due Tomorrow!", message);
        }
    }
}

【问题讨论】:

    标签: c# asp.net-core scheduled-tasks fluentscheduler


    【解决方案1】:

    从 IJob 的 source code 看来,实现 IJob 的类需要有一个无参数的默认构造函数。由于 FluentScheduler 也是 supports lambdas,因此让您的依赖注入库创建您的对象可能会更容易,然后像这样调用 Execute 方法:

    var myJob = new MyJob(new ApplicationDbContext());
    Schedule(() => myJob.Execute()).ToRunEvery(1).Days().At(21, 15);
    

    或者自己调用构造函数:

    // Schedule a job using a factory method and pass parameters to the constructor.
    Schedule(() => new MyJob(new ApplicationDbContext())).ToRunNow().AndEvery(2).Seconds();
    

    【讨论】:

    • new ApplicationDbContext() 不应该是已经从 ConfigureServices 实例化的对象吗?
    • 这只是一个占位符,但是您正在获取ApplicationDbContext
    • new MyJob(new ApplicationDbContext()) 不接受。与没有 mssing 方法错误相同的错误
    • 您目前如何获得ApplicationDbContext 的实例?
    • 我做不到。那就是问题所在。不知何故,我可以将已经运行的 DbContext 实例注入到 Schedule 中,我将能够完成我的任务。
    【解决方案2】:

    注意 - 在文档中,它说不要使用 IJobFactory,因为它很快就会被弃用,但它在过去 3.4 个月的生产中为我工作 - https://github.com/fluentscheduler/FluentScheduler/issues/71

    我在 Windows 控制台应用程序中使用 AutofacFluent Scheduler,它使用 TopShelf 将其作为 Windows 服务运行。

    要在 FluentScheduler 中使用依赖注入,您必须设置一个作业工厂来解决依赖关系。

    所以首先通过实现IJobFactory 来设置JobFactory

    像这样-

    public class MyJobFactory : IJobFactory
    {
        public IJob GetJobInstance<T>() where T : IJob
        {
            return MyContainer.GetJobInstance<T>();
        }
    }
    

    现在在 Job Factory 中,因为我正在使用 Autofac 我正在设置一个具有 2 种方法的容器

    1 - ConfigureDependencies() - 只调用一次来设置容器:

    2 - GetJobInstance() - 由 MyJobFactory 调用以解析 Job 实例

    public class MyContainer
    {
        public static IContainer Container { get; set; }
    
        public static void ConfigureDependencies()
        {
            var builder = new ContainerBuilder();
    
            // Jobs
            builder.RegisterType<MyJob>().As<MyJob>();
    
            // DB Contexts
    
            // Others
    
            Container = builder.Build();
        }
    
    
        public static IJob GetJobInstance<T>() where T : IJob
        {
            return Container.Resolve<T>();
        }
    }
    

    然后当你启动你的应用程序/服务时,它会看起来像这样。

    public void Start()
    {
        // Configure Dependency Injection
        MyContainer.ConfigureDependencies();
    
        // Setup the Fluent Scheduler - 
        JobManager.JobFactory = new MyJobFactory();
    
        JobManager.UseUtcTime();
    
        JobManager.Initialize(new MyJobRegistry());     
    }
    

    然后我通过添加 2 个构造函数创建了一个 Job -

    1 - 无参数

    2 - 带有注入对象的构造函数

    例如

    public class MyJob : IJob
    {
        public static string JobName = "MyJob";
    
        private readonly ICompaniesProvider _companiesProvider;
    
        // parameter-less constructor
        public MyJob() { }
    
        // injecting HERE
        public MyJob(ICompaniesProvider companiesProvider)
        {
            _companiesProvider = companiesProvider;
    
        }
    
        public void Execute()
        {
    
    
    
        }
    }
    

    编辑

    由于IJobFactory 已弃用,您可以直接调用容器并获取一个作业实例,然后再将其添加到作业管理器

    public void Start()
    {
        // Configure Dependency Injection
        MyContainer.ConfigureDependencies();
    
        JobManager.UseUtcTime();
        JobManager.Start();
    
        var myJob = MyContainer.GetJobInstance<MyJob>();
    
        Action<Schedule> schedule = s => s.ToRunEvery(1).Hours().At(0);
    
        JobManager.AddJob(myJob, schedule);
    }
    

    【讨论】:

      【解决方案3】:

      我在这里发布了类似的答案-https://stackoverflow.com/a/51167073/7166609

      我已经使用了Application服务范围,注入到注册表的构造函数中,并使用这个范围来获取我的作业类的execute方法中的对象。

      【讨论】:

        猜你喜欢
        • 2011-02-02
        • 2018-06-17
        • 1970-01-01
        • 2019-04-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-04
        相关资源
        最近更新 更多