Я создаю основное веб-приложение .net, которое требует выполнения фоновых задач. Чтобы избежать использования внешних триггеров cron, мы решили использовать Hangfire; это красивый пакет для использования, он делает именно то, что нужно, а затем убирается с пути; -)
Чтобы сохранить вещи в чистоте, я стараюсь придерживаться принципов чистой архитектуры дяди Боба и максимально отделить мой ApplicationCore от инфраструктуры. Поскольку Hangfire - это деталь реализации, в идеале он должен находиться в проекте инфраструктуры вместе с доступом к базе данных, очередями сообщений и т. Д. С интерфейсом в ApplicationCore, который может использовать мой домен.
Для основного клиента, выполняющего повторяющиеся и фоновые задания, это было довольно просто сделать, и я закончил с этим как мой Interface
namespace ApplicationCore.Interfaces
{
using System;
using System.Linq.Expressions;
using System.Threading.Tasks;
public interface IBackgroundJobClient
{
void AddOrUpdate<T>(
string recurringJobId,
Expression<Func<T, Task>> methodCall,
string cronExpression);
void RemoveIfExists(string recurringJobId);
}
}
Реализация представляет собой простую оболочку для этих методов, которая использует RecurringJob
namespace Infrastructure.BackgroundJobs
{
using System;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Hangfire;
using IBackgroundJobClient = ApplicationCore.Interfaces.IBackgroundJobClient;
public class HangfireBackgroundJobClient : IBackgroundJobClient
{
public void AddOrUpdate<T>(
string recurringJobId,
Expression<Func<T, Task>> methodCall,
string cronExpression)
{
RecurringJob.AddOrUpdate<T>(recurringJobId, methodCall, cronExpression);
}
public void RemoveIfExists(string recurringJobId)
{
RecurringJob.RemoveIfExists(recurringJobId);
}
}
У меня возникла проблема с настройкой RecurringJob
с поставляемым списком отмены. Однако я не вижу простого способа сделать это, не раскрывая базовые объекты IJobCancellationToken
и JobCancellationToken
в моем коде ApplicationCore ...?
В настоящее время у меня есть оболочка для JobCancellationToken
в моей Инфраструктуре.
namespace Infrastructure.BackgroundJobs
{
public class BackgroundJobCancellationToken : JobCancellationToken
{
public BackgroundJobCancellationToken(bool canceled): base(canceled)
{
}
}
}
Интерфейс в моем ApplicationCore, который копирует один из Hangfire.
namespace ApplicationCore.Interfaces
{
using System.Threading;
public interface IJobCancellationToken
{
CancellationToken ShutdownToken { get; }
void ThrowIfCancellationRequested();
}
}
Затем он используется методом, который я хочу выполнить как задание, используя cancellationToken.ShutdownToken
для перехода к другим методам, требующим cancellationToken.
public async Task GenerateSubmission(Guid SetupGuidId, IJobCancellationToken cancellationToken)
{
try
{
// Sort out the entities that we'll need
var setup = await this.SetupRepository.GetByGuidIdAsync(SetupGuidId);
var forecast = await this.GetCurrentForecastForSetup(setup, DateTime.UtcNow, cancellationToken.ShutdownToken);
// Other Code
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
Что, в свою очередь, включено в другом месте, вызывая
public async Task<Setup> EnableSetup(Setup setup)
{
setup.Enable();
this.jobClient
.AddOrUpdate<IForecastService>(
setup.GuidId.ToString(),
f => f.GenerateSubmission(setup.GuidId, null),
"45 */2 * * *");
await this.setupRepository.UpdateAsync(setup);
return setup;
}
Это должно быть сделано с DomainEvents и Обработчиками, но по одному шагу за раз: -)
Есть ли более чистый, лучший и простой способ сделать это без прямой зависимости от Hangfire в моем ApplicationCore?
Если вышеуказанная настройка сработает, я оставлю комментарий к этому вопросу.