diff --git a/src/core/Elsa.Jobs/Abstractions/Job.cs b/src/core/Elsa.Jobs/Abstractions/Job.cs new file mode 100644 index 000000000..f3136142b --- /dev/null +++ b/src/core/Elsa.Jobs/Abstractions/Job.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; +using Elsa.Jobs.Models; + +namespace Elsa.Jobs.Contracts; + +public abstract class Job : IJob +{ + ValueTask IJob.ExecuteAsync(JobExecutionContext context) => ExecuteAsync(context); + + protected virtual ValueTask ExecuteAsync(JobExecutionContext context) + { + Execute(context); + return ValueTask.CompletedTask; + } + + protected virtual void Execute(JobExecutionContext context) + { + } +} \ No newline at end of file diff --git a/src/core/Elsa.Jobs/Abstractions/JobHandler.cs b/src/core/Elsa.Jobs/Abstractions/JobHandler.cs deleted file mode 100644 index 40a2c132e..000000000 --- a/src/core/Elsa.Jobs/Abstractions/JobHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Elsa.Jobs.Contracts; - -namespace Elsa.Jobs.Abstractions; - -public abstract class JobHandler : IJobHandler where T : IJob -{ - public bool GetSupports(IJob job) => job is T; - - public virtual Task HandleAsync(IJob job, CancellationToken cancellationToken) => HandleAsync((T)job, cancellationToken); - - protected virtual Task HandleAsync(T job, CancellationToken cancellationToken) - { - Handle(); - return Task.CompletedTask; - } - - protected virtual void Handle() - { - } -} \ No newline at end of file diff --git a/src/core/Elsa.Jobs/Contracts/IJob.cs b/src/core/Elsa.Jobs/Contracts/IJob.cs index ddab62312..e18d55663 100644 --- a/src/core/Elsa.Jobs/Contracts/IJob.cs +++ b/src/core/Elsa.Jobs/Contracts/IJob.cs @@ -1,6 +1,9 @@ +using System.Threading.Tasks; +using Elsa.Jobs.Models; + namespace Elsa.Jobs.Contracts; public interface IJob { - string JobId { get; } + ValueTask ExecuteAsync(JobExecutionContext context); } \ No newline at end of file diff --git a/src/core/Elsa.Jobs/Contracts/IJobHandler.cs b/src/core/Elsa.Jobs/Contracts/IJobHandler.cs deleted file mode 100644 index dfdf1a8c7..000000000 --- a/src/core/Elsa.Jobs/Contracts/IJobHandler.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; - -namespace Elsa.Jobs.Contracts; - -public interface IJobHandler -{ - bool GetSupports(IJob job); - Task HandleAsync(IJob job, CancellationToken cancellationToken); -} \ No newline at end of file diff --git a/src/core/Elsa.Jobs/Contracts/IJobScheduler.cs b/src/core/Elsa.Jobs/Contracts/IJobScheduler.cs index d8be87107..f17859902 100644 --- a/src/core/Elsa.Jobs/Contracts/IJobScheduler.cs +++ b/src/core/Elsa.Jobs/Contracts/IJobScheduler.cs @@ -5,7 +5,7 @@ namespace Elsa.Jobs.Contracts; public interface IJobScheduler { - Task ScheduleAsync(IJob job, ISchedule schedule, string[]? groupKeys = default, CancellationToken cancellationToken = default); - Task UnscheduleAsync(IJob job, CancellationToken cancellationToken = default); + Task ScheduleAsync(IJob job, string name, ISchedule schedule, string[]? groupKeys = default, CancellationToken cancellationToken = default); + Task UnscheduleAsync(string name, CancellationToken cancellationToken = default); Task ClearAsync(string[]? groupKeys = default, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/core/Elsa.Jobs/Extensions/ServiceCollectionExtensions.cs b/src/core/Elsa.Jobs/Extensions/ServiceCollectionExtensions.cs index 3c6c7ff28..9cbf65c60 100644 --- a/src/core/Elsa.Jobs/Extensions/ServiceCollectionExtensions.cs +++ b/src/core/Elsa.Jobs/Extensions/ServiceCollectionExtensions.cs @@ -6,7 +6,7 @@ namespace Elsa.Jobs.Extensions; public static class ServiceCollectionExtensions { - public static IServiceCollection AddJobs(this IServiceCollection services, IJobSchedulerProvider schedulerProvider, IJobQueueProvider queueProvider) + public static IServiceCollection AddJobServices(this IServiceCollection services, IJobSchedulerProvider schedulerProvider, IJobQueueProvider queueProvider) { services .AddSingleton() @@ -16,6 +16,4 @@ public static class ServiceCollectionExtensions queueProvider.ConfigureServices(services); return services; } - - public static IServiceCollection AddJobHandler(this IServiceCollection services) where T : class, IJobHandler => services.AddSingleton(); } \ No newline at end of file diff --git a/src/core/Elsa.Jobs/Models/JobExecutionContext.cs b/src/core/Elsa.Jobs/Models/JobExecutionContext.cs new file mode 100644 index 000000000..5fff55acb --- /dev/null +++ b/src/core/Elsa.Jobs/Models/JobExecutionContext.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Jobs.Models; + +public class JobExecutionContext +{ + public JobExecutionContext(IServiceProvider serviceProvider, CancellationToken cancellation) + { + ServiceProvider = serviceProvider; + Cancellation = cancellation; + } + + public IServiceProvider ServiceProvider { get; } + public CancellationToken Cancellation { get; } + + public T GetRequiredService() where T : notnull => ServiceProvider.GetRequiredService(); +} \ No newline at end of file diff --git a/src/core/Elsa.Jobs/Services/JobRunner.cs b/src/core/Elsa.Jobs/Services/JobRunner.cs index 7e420541d..8eb54d807 100644 --- a/src/core/Elsa.Jobs/Services/JobRunner.cs +++ b/src/core/Elsa.Jobs/Services/JobRunner.cs @@ -1,28 +1,23 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Elsa.Jobs.Contracts; +using Elsa.Jobs.Models; namespace Elsa.Jobs.Services; public class JobRunner : IJobRunner { - private readonly IEnumerable _handlers; + private readonly IServiceProvider _serviceProvider; - public JobRunner(IEnumerable handlers) + public JobRunner(IServiceProvider serviceProvider) { - _handlers = handlers; + _serviceProvider = serviceProvider; } public async Task RunJobAsync(IJob job, CancellationToken cancellationToken = default) { - var handler = _handlers.FirstOrDefault(x => x.GetSupports(job)); - - if (handler == null) - throw new NotSupportedException($"The specified job of type {job.GetType().Name} does not have a handler"); - - await handler.HandleAsync(job, cancellationToken); + var context = new JobExecutionContext(_serviceProvider, cancellationToken); + await job.ExecuteAsync(context); } } \ No newline at end of file diff --git a/src/modules/Elsa.Modules.Quartz/Services/QuartzJobScheduler.cs b/src/modules/Elsa.Modules.Quartz/Services/QuartzJobScheduler.cs index e37dc3165..bdcaa076a 100644 --- a/src/modules/Elsa.Modules.Quartz/Services/QuartzJobScheduler.cs +++ b/src/modules/Elsa.Modules.Quartz/Services/QuartzJobScheduler.cs @@ -24,16 +24,16 @@ public class QuartzJobScheduler : IJobScheduler _logger = logger; } - public async Task ScheduleAsync(IJob job, IElsaSchedule schedule, string[]? groupKeys, CancellationToken cancellationToken = default) + public async Task ScheduleAsync(IJob job, string name, IElsaSchedule schedule, string[]? groupKeys, CancellationToken cancellationToken = default) { - var quartzTrigger = CreateTrigger(job, schedule, groupKeys); + var quartzTrigger = CreateTrigger(job, name, schedule, groupKeys); await ScheduleJob(quartzTrigger, cancellationToken); } - public async Task UnscheduleAsync(IJob job, CancellationToken cancellationToken = default) + public async Task UnscheduleAsync(string name, CancellationToken cancellationToken = default) { var scheduler = await _schedulerFactory.GetScheduler(cancellationToken); - var triggerKey = new TriggerKey(job.JobId); + var triggerKey = new TriggerKey(name); await scheduler.UnscheduleJob(triggerKey, cancellationToken); } @@ -60,11 +60,11 @@ public class QuartzJobScheduler : IJobScheduler } } - private ITrigger CreateTrigger(IJob job, IElsaSchedule schedule, string[]? groupKeys) + private ITrigger CreateTrigger(IJob job, string name, IElsaSchedule schedule, string[]? groupKeys) { var jobName = job.GetType().Name; var groupKey = BuildGroupKey(groupKeys); - var triggerKey = new TriggerKey(job.JobId, groupKey); + var triggerKey = new TriggerKey(name, groupKey); var json = _jobSerializer.Serialize(job); var builder = TriggerBuilder.Create().ForJob(jobName).WithIdentity(triggerKey).UsingJobData(JobDataKey, json); diff --git a/src/modules/Elsa.Modules.Scheduling/Extensions/ServiceCollectionExtensions.cs b/src/modules/Elsa.Modules.Scheduling/Extensions/ServiceCollectionExtensions.cs index d516afd3f..667602ec3 100644 --- a/src/modules/Elsa.Modules.Scheduling/Extensions/ServiceCollectionExtensions.cs +++ b/src/modules/Elsa.Modules.Scheduling/Extensions/ServiceCollectionExtensions.cs @@ -15,8 +15,6 @@ public static class ServiceCollectionExtensions services .AddSingleton() .AddSingleton() - .AddJobHandler() - .AddJobHandler() .AddNotificationHandlersFrom() .AddHostedService(); diff --git a/src/modules/Elsa.Modules.Scheduling/Jobs/ResumeWorkflowJob.cs b/src/modules/Elsa.Modules.Scheduling/Jobs/ResumeWorkflowJob.cs index feb2db7a8..02f2d4ed4 100644 --- a/src/modules/Elsa.Modules.Scheduling/Jobs/ResumeWorkflowJob.cs +++ b/src/modules/Elsa.Modules.Scheduling/Jobs/ResumeWorkflowJob.cs @@ -1,26 +1,33 @@ -using System.Threading; +using System.Text.Json.Serialization; using System.Threading.Tasks; using Elsa.Models; using Elsa.Runtime.Contracts; using Elsa.Runtime.Models; -using Elsa.Jobs.Abstractions; using Elsa.Jobs.Contracts; +using Elsa.Jobs.Models; namespace Elsa.Modules.Scheduling.Jobs; -public record ResumeWorkflowJob(string WorkflowInstanceId, Bookmark Bookmark) : IJob +public class ResumeWorkflowJob : Job { - public string JobId => $"Bookmark:{Bookmark.Id}"; -} - -public class ResumeWorkflowJobHandler : JobHandler -{ - private readonly IWorkflowInvoker _workflowInvoker; - public ResumeWorkflowJobHandler(IWorkflowInvoker workflowInvoker) => _workflowInvoker = workflowInvoker; - - protected override async Task HandleAsync(ResumeWorkflowJob job, CancellationToken cancellationToken) + [JsonConstructor] + public ResumeWorkflowJob() { - var request = new DispatchWorkflowInstanceRequest(job.WorkflowInstanceId, job.Bookmark); - await _workflowInvoker.DispatchAsync(request, cancellationToken); + } + + public ResumeWorkflowJob(string workflowInstanceId, Bookmark bookmark) + { + WorkflowInstanceId = workflowInstanceId; + Bookmark = bookmark; + } + + public string WorkflowInstanceId { get; set; } = default!; + public Bookmark Bookmark { get; set; } = default!; + + protected override async ValueTask ExecuteAsync(JobExecutionContext context) + { + var request = new DispatchWorkflowInstanceRequest(WorkflowInstanceId, Bookmark); + var workflowInvoker = context.GetRequiredService(); + await workflowInvoker.DispatchAsync(request, context.Cancellation); } } \ No newline at end of file diff --git a/src/modules/Elsa.Modules.Scheduling/Jobs/RunWorkflowJob.cs b/src/modules/Elsa.Modules.Scheduling/Jobs/RunWorkflowJob.cs index b8b11349d..459fd9838 100644 --- a/src/modules/Elsa.Modules.Scheduling/Jobs/RunWorkflowJob.cs +++ b/src/modules/Elsa.Modules.Scheduling/Jobs/RunWorkflowJob.cs @@ -1,29 +1,30 @@ -using System.Threading; +using System.Text.Json.Serialization; using System.Threading.Tasks; using Elsa.Runtime.Contracts; using Elsa.Runtime.Models; -using Elsa.Jobs.Abstractions; using Elsa.Jobs.Contracts; +using Elsa.Jobs.Models; namespace Elsa.Modules.Scheduling.Jobs; -public record RunWorkflowJob(string WorkflowId) : IJob +public class RunWorkflowJob : Job { - public string JobId => WorkflowId; -} - -public class RunWorkflowJobHandler : JobHandler -{ - private readonly IWorkflowInvoker _workflowInvoker; - - public RunWorkflowJobHandler(IWorkflowInvoker workflowInvoker) + [JsonConstructor] + public RunWorkflowJob() { - _workflowInvoker = workflowInvoker; } - protected override async Task HandleAsync(RunWorkflowJob job, CancellationToken cancellationToken) + public RunWorkflowJob(string workflowId) { - var request = new DispatchWorkflowDefinitionRequest(job.WorkflowId, 1); - await _workflowInvoker.DispatchAsync(request, cancellationToken); + WorkflowId = workflowId; + } + + public string WorkflowId { get; set; } = default!; + + protected override async ValueTask ExecuteAsync(JobExecutionContext context) + { + var request = new DispatchWorkflowDefinitionRequest(WorkflowId, 1); + var workflowInvoker = context.GetRequiredService(); + await workflowInvoker.DispatchAsync(request, context.Cancellation); } } \ No newline at end of file