FluentScheduler dependency injection in constructor
原文链接:https://www.thetopsites.net/article/50617160.shtml
I am experimenting with FluentScheduler for some background tasks in ASP.net Core API.
The job should send push notifications every day at a particular time interval based on few criteria. I had gone through the document and implemented a test functionality to print some output in the console window. It worked as expected with predicted time interval.
But the actual job I am going to do with that involves database context which provides necessary information to perform the criteria to send out the notifications.
My problem is I am unable to use constructor with parameter in MyJob
class which is throwing missing method exception
PS: As per this article from Scott Hanselman, FluentScheduler seems to be quite famous but I could not get any help from online communities. But obviously, it's quite easy to grasp.
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); } } }
From the source code for IJob, it looks like your class that implements IJob needs to have a parameterless default constructor. Since FluentScheduler also supports lambdas, it may be easier to have your dependency injection library create your object, then call the Execute
method like so:
var myJob = new MyJob(new ApplicationDbContext()); Schedule(() => myJob.Execute()).ToRunEvery(1).Days().At(21, 15);
Or call the constructor yourself:
// Schedule a job using a factory method and pass parameters to the constructor. Schedule(() => new MyJob(new ApplicationDbContext())).ToRunNow().AndEvery(2).Seconds();
FluentScheduler dependency injection in constructor, Net and much more using FluentScheduler I'm trying to use a Core 2.0+ ? I want to use my services and resolve them dependencies. You can use constructor injection for your jobs and get that seamless ASP.NET Core As you said, your job is registered with a scoped lifetime 'Web API Request'. This means that a instance of a dependency is created by the container per request and it is disposed when the request ends. Your job is running inside FluentScheduler and is not linked with any request.
Note - In the documentation it says not to use IJobFactory because it is going to be deprecated soon, but it is working for me in Production for past 3,4 months - https://github.com/fluentscheduler/FluentScheduler/issues/71
I am using Autofac
and Fluent Scheduler
in a Windows Console Application which uses TopShelf
to run it as a Windows Service.
To use Dependency Injection in FluentScheduler you have to setup a Job Factory to resolve the dependencies.
So first setup a JobFactory
by implementing IJobFactory
Like this -
public class MyJobFactory : IJobFactory { public IJob GetJobInstance<T>() where T : IJob { return MyContainer.GetJobInstance<T>(); } }
Now in the Job Factory because I'm using Autofac
I am setting up a Container which has 2 methods
1 - ConfigureDependencies()
- Which is only called once to setup the Container:
2 - GetJobInstance()
- Which is called by the MyJobFactory
to resolve t a Job instance
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>(); } }
Then when you Start you application/service, it will look something like this.
public void Start() { // Configure Dependency Injection MyContainer.ConfigureDependencies(); // Setup the Fluent Scheduler - JobManager.JobFactory = new MyJobFactory(); JobManager.UseUtcTime(); JobManager.Initialize(new MyJobRegistry()); }
Then I've created a Job by adding 2 constructors -
1 - Parameter-less
2 - The constructor with Injected Objects
e.g.
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() { } }
EDIT
Because of deprecated issue of IJobFactory
, you could just call the Container Directly and get a Job Instance before adding it to the JobManager
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); }