Jak planować zadania przy użyciu Quartz.NET w ASP.NET Core

Podczas pracy z aplikacjami internetowymi często będziesz musiał wykonywać pewne zadania w tle. W niektórych przypadkach będą to zadania, które powinny być wykonywane w określonych odstępach czasu.

Quartz.NET to port .NET o otwartym kodzie źródłowym popularnej platformy planowania zadań Java. Jest używany przez długi czas i zapewnia doskonałe wsparcie dla pracy z wyrażeniami Cron. Możesz dowiedzieć się więcej o Quartz.NET we wcześniejszym poście tutaj. 

W tym artykule omówiono sposób pracy z Quartz.NET w ASP.NET Core w celu planowania zadań w tle.

Aby pracować z przykładami kodu przedstawionymi w tym artykule, w systemie powinien być zainstalowany program Visual Studio 2019. Jeśli nie masz jeszcze kopii, możesz pobrać program Visual Studio 2019 tutaj. 

Utwórz projekt interfejsu API ASP.NET Core

Po pierwsze, utwórzmy projekt ASP.NET Core w programie Visual Studio. Zakładając, że w systemie jest zainstalowany program Visual Studio 2019, wykonaj czynności opisane poniżej, aby utworzyć nowy projekt ASP.NET Core w programie Visual Studio.

  1. Uruchom środowisko IDE programu Visual Studio.
  2. Kliknij „Utwórz nowy projekt”.
  3. W oknie „Utwórz nowy projekt” wybierz „Aplikacja sieci Web ASP.NET Core” z wyświetlonej listy szablonów.
  4. Kliknij Następny.
  5. W oknie „Konfiguruj nowy projekt” pokazanym obok podaj nazwę i lokalizację nowego projektu.
  6. Kliknij Utwórz.
  7. W oknie „Tworzenie nowej aplikacji sieci Web ASP.NET Core” wybierz .NET Core jako środowisko wykonawcze i ASP.NET Core 2.2 (lub nowszy) z listy rozwijanej u góry. Będę tutaj używać ASP.NET Core 3.0.
  8. Wybierz „API” jako szablon projektu, aby utworzyć nową aplikację ASP.NET Core API. 
  9. Upewnij się, że pola wyboru „Włącz obsługę Dockera” i „Konfiguruj dla HTTPS” są odznaczone, ponieważ nie będziemy tutaj używać tych funkcji.
  10. Upewnij się, że uwierzytelnianie jest ustawione na „Brak uwierzytelniania”, ponieważ nie będziemy też używać uwierzytelniania.
  11. Kliknij Utwórz. 

Spowoduje to utworzenie nowego projektu interfejsu API ASP.NET Core w programie Visual Studio. Wybierz folder Controllers solution w oknie Solution Explorer i kliknij „Dodaj -> Kontroler…”, aby utworzyć nowy kontroler o nazwie DefaultController.

Następnie, aby pracować z Quartz, należy zainstalować pakiet Quartz z NuGet. Możesz to zrobić za pośrednictwem menedżera pakietów NuGet w środowisku IDE programu Visual Studio 2019 lub wykonując następujące polecenie w konsoli Menedżera pakietów NuGet:

Zainstaluj pakiet Quartz

Quartz.NET zadania, wyzwalacze i harmonogramy 

Trzy główne pojęcia w Quartz.NET to zadania, wyzwalacze i harmonogramy. Zadanie zawiera kod do wykonania zadania lub zadania do wykonania. Zadanie jest reprezentowane przez klasę implementującą interfejs IJob. Wyzwalacz służy do określania harmonogramu i innych szczegółów zadania. Możesz skorzystać z wyzwalacza, aby określić sposób wykonania zadania. Harmonogram to komponent odpowiedzialny za odpytywanie i wykonywanie zadań na podstawie wstępnie zdefiniowanych harmonogramów.

Utwórz harmonogram przy użyciu Quartz.NET

Należy zauważyć, że w aplikacji można mieć wiele programów planujących. Jednak ze względu na prostotę użyjemy tutaj tylko jednego harmonogramu. Poniższy fragment kodu ilustruje sposób tworzenia instancji programu planującego.

var scheduleer = StdSchedulerFactory.GetDefaultScheduler (). GetAwaiter (). GetResult ();

Po utworzeniu harmonogramu można użyć następującego kodu w metodzie ConfigureServices pliku Startup.cs, aby dodać wystąpienie harmonogramu jako usługę pojedynczą.

services.AddSingleton (program planujący);

Uruchamiaj i zatrzymuj harmonogram przy użyciu Quartz.NET

Aby uruchomić i zatrzymać harmonogram, skorzystamy z usługi hostingowej. Aby to zrobić, musisz utworzyć klasę, która implementuje interfejs IHostingService, jak pokazano we fragmencie kodu podanym poniżej.

public class CustomQuartzHostedService: IHostedService

{

        prywatny tylko do odczytu IScheduler _scheduler;

        public CustomQuartzHostedService (IScheduler Scheduler)

        {

            _scheduler = planista;

        }

        publiczne zadanie asynchroniczne StartAsync (CancellationToken cancellationToken)

        {

            await _scheduler? .Start (cancellationToken);

        }

        publiczne zadanie asynchroniczne StopAsync (CancellationToken cancellationToken)

        {

            await _scheduler? .Shutdown (cancellationToken);

        }

 }

Należy pamiętać, że usługę hostowaną należy zarejestrować w kolekcji usług w metodzie ConfigureServices przy użyciu fragmentu kodu podanego poniżej.

services.AddHostedService ();

Oto zaktualizowana metoda ConfigureServices w celach informacyjnych:

public void ConfigureServices (usługi IServiceCollection)

{

    services.AddControllers ();

    var planista =

    StdSchedulerFactory.GetDefaultScheduler (). GetAwaiter (). GetResult ();

    services.AddSingleton (program planujący);

    services.AddHostedService ();

}

Utwórz zadanie za pomocą Quartz.NET

Jak powiedziałem wcześniej, zadanie to klasa, która implementuje interfejs IJob i zawiera metodę Execute (). Metoda Execute () akceptuje instancję typu IJobExecutionContext.

Poniższy fragment kodu ilustruje klasę zadania, która zawiera również asynchroniczną metodę Execute (). Ta metoda zawiera kod odpowiadający zadaniu, które ma wykonać Twoje zadanie.

[DisallowConcurrentExecution]

public class NotificationJob: IJob

    {

        prywatny tylko do odczytu ILogger _logger;

        public NotificationJob (rejestrator ILogger)

        {

            _logger = logger;

        }

        publiczne wykonanie zadania (kontekst IJobExecutionContext)

        {

            _logger.LogInformation ("Witaj świecie!");

            return Task.CompletedTask;

        }

    }

Utwórz fabrykę zadań za pomocą Quartz.NET

Fabryka zadań to klasa, która dziedziczy interfejs IJobFactory i implementuje metody NewJob () i ReturnJob (). Poniższy fragment kodu może służyć do tworzenia klasy fabrycznej, która może tworzyć i zwracać wystąpienie zadania.

public class CustomQuartzJobFactory: IJobFactory

    {

        prywatny tylko do odczytu IServiceProvider _serviceProvider;

        public CustomQuartzJobFactory (IServiceProvider serviceProvider)

        {

            _serviceProvider = serviceProvider;

        }

        public IJob NewJob (TriggerFiredBundle triggerFiredBundle,

        IScheduler Scheduler)

        {

            var jobDetail = triggerFiredBundle.JobDetail;

            return (IJob) _serviceProvider.GetService (jobDetail.JobType);

        }

        public void ReturnJob (IJob job) {}

    }

Należy pamiętać, że ta implementacja nie korzysta z puli zadań. Aby korzystać z puli zadań, należy zmienić metodę NewJob (), a następnie zaimplementować metodę ReturnJob ().

Utwórz klasę JobMetadata, aby przechowywać metadane zadania

Użyjemy niestandardowej klasy do przechowywania metadanych związanych z zadaniem, tj. Identyfikatora, nazwy itp. Poniższa klasa reprezentuje klasę metadanych zadania.

klasa publiczna JobMetadata

    {

        public Guid JobId {get; zestaw; }

        public Type JobType {get; }

        public string JobName {get; }

        public string CronExpression {get; }

        public JobMetadata (identyfikator Guid, Type jobType, string jobName,

        string cronExpression)

        {

            JobId = Id;

            JobType = jobType;

            JobName = jobName;

            CronExpression = cronExpression;

        }

    }

Utwórz usługę hostowaną, aby uruchamiać i zatrzymywać program planujący Quartz.NET

Następnie musimy zaimplementować usługę hostowaną. Usługa hostowana to klasa, która implementuje interfejs IHostedService i uruchamia program planujący Quartz. Poniższy kod ilustruje niestandardową klasę usługi hostowanej.

public class CustomQuartzHostedService: IHostedService

    {

        private readonly ISchedulerFactory scheduleerFactory;

        prywatne tylko do odczytu IJobFactory jobFactory;

        prywatne tylko do odczytu JobMetadata jobMetadata;

        public CustomQuartzHostedService (ISchedulerFactory

            scheduleerFactory,

            JobMetadata jobMetadata,

            IJobFactory jobFactory)

        {

            this.schedulerFactory = scheduleerFactory;

            this.jobMetadata = jobMetadata;

            this.jobFactory = jobFactory;

        }

        public IScheduler Scheduler {get; zestaw; }

        publiczne zadanie asynchroniczne StartAsync (CancellationToken cancellationToken)

        {

            Scheduler = await scheduleerFactory.GetScheduler ();

            Scheduler.JobFactory = jobFactory;

            var job = CreateJob (jobMetadata);

            var trigger = CreateTrigger (jobMetadata);

            await Scheduler.ScheduleJob (zadanie, wyzwalacz, cancellationToken);

            await Scheduler.Start (cancellationToken);

        }

        publiczne zadanie asynchroniczne StopAsync (CancellationToken cancellationToken)

        {

            await Scheduler? .Shutdown (cancellationToken);

        }

        prywatny ITrigger CreateTrigger (JobMetadata jobMetadata)

        {

            return TriggerBuilder.Create ()

            .WithIdentity (jobMetadata.JobId.ToString ())

            .WithCronSchedule (jobMetadata.CronExpression)

            .WithDescription ($ "{jobMetadata.JobName}")

            .Budować();

        }

        prywatne IJobDetail CreateJob (JobMetadata jobMetadata)

        {

            return JobBuilder

            .Create (jobMetadata.JobType)

            .WithIdentity (jobMetadata.JobId.ToString ())

            .WithDescription ($ "{jobMetadata.JobName}")

            .Budować();

        }

    }

Poniższy fragment kodu przedstawia pełny kod metody ConfigureServices klasy Startup.

public void ConfigureServices (usługi IServiceCollection)

{

services.AddControllers ();

services.AddSingleton ();

services.AddSingleton ();

services.AddSingleton ();

services.AddSingleton (new JobMetadata (Guid.NewGuid (), typeof (NotificationJob), "Notification Job", "0/10 * * * *?"));

services.AddHostedService ();

}

I to wszystko, co musisz zrobić! Podczas wykonywania aplikacji zauważysz, że metoda Execute () klasy NotificationJob jest uruchamiana co 10 sekund.

Quartz.NET to dobry wybór do wdrażania harmonogramów w aplikacjach. Możesz skorzystać z funkcji trwałości w Quartz.NET, aby przechowywać swoje zadania w bazie danych, takiej jak SQL Server, PostgreSQL lub SQLite.