From 67904ae2e4fa1c15870223ecd93ee86b01907403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SAL=C4=B0H=20=C3=96ZKARA?= Date: Tue, 10 Mar 2026 12:58:01 +0300 Subject: [PATCH] Add anonymous background job support Introduce anonymous/background-job-by-name support: add AnonymousJobArgs, IAnonymousJobHandlerRegistry and AnonymousJobHandlerRegistry, and an AnonymousJobExecutorAsyncBackgroundJob to execute JSON-based anonymous handlers. AbpBackgroundJobOptions now stores anonymous handlers and exposes registration/query helpers. Updated background job managers (Default, Hangfire, Quartz, RabbitMQ, TickerQ) to wrap registered anonymous jobs into AnonymousJobArgs when enqueuing and to depend on the handler registry and JSON serializer where needed. Removed the old dynamic handler types/APIs (DynamicBackgroundJobContext, IDynamicBackgroundJobHandlerProvider, DynamicBackgroundJobHandlerProvider) and related dynamic handling code; BackgroundJobConfiguration simplified (JobType non-nullable) and JobExecutionContext no longer carries JobName. Tests and demo code updated to use anonymous handlers and tracking. This change centralizes runtime-registered handlers keyed by job name and standardizes anonymous job payloads as JSON. --- .../hooks/state/continual-learning-index.json | 9 +++ .cursor/hooks/state/continual-learning.json | 8 +++ .../BackgroundJobs/AbpBackgroundJobOptions.cs | 30 +++++---- .../Abp/BackgroundJobs/AnonymousJobArgs.cs | 17 +++++ .../AnonymousJobHandlerRegistry.cs | 56 ++++++++++++++++ .../BackgroundJobConfiguration.cs | 19 +----- .../BackgroundJobs/BackgroundJobExecuter.cs | 65 ------------------- .../DynamicBackgroundJobContext.cs | 25 ------- .../DynamicBackgroundJobHandlerProvider.cs | 36 ---------- .../IAnonymousJobHandlerRegistry.cs | 18 +++++ .../IDynamicBackgroundJobHandlerProvider.cs | 15 ----- .../Abp/BackgroundJobs/JobExecutionContext.cs | 6 +- .../Hangfire/HangfireBackgroundJobManager.cs | 17 ++++- .../Hangfire/HangfireJobExecutionAdapter.cs | 4 +- .../Quartz/QuartzBackgroundJobManager.cs | 17 ++++- .../Quartz/QuartzJobExecutionAdapter.cs | 2 +- .../Abp/BackgroundJobs/RabbitMQ/JobQueue.cs | 5 +- .../RabbitMQ/RabbitMqBackgroundJobManager.cs | 19 +++++- .../TickerQ/AbpBackgroundJobsTickerQModule.cs | 2 +- .../TickerQ/AbpTickerQBackgroundJobManager.cs | 21 +++++- .../AnonymousJobExecutorAsyncBackgroundJob.cs | 35 ++++++++++ .../Abp/BackgroundJobs/BackgroundJobWorker.cs | 5 +- .../DefaultBackgroundJobManager.cs | 15 +++++ .../AbpBackgroundJobsTestModule.cs | 8 +-- .../AnonymousJobExecutionTracker.cs | 8 +++ .../BackgroundJobExecuter_Tests.cs | 40 ++++++------ .../BackgroundJobManager_Tests.cs | 10 ++- .../DynamicJobExecutionTracker.cs | 8 --- .../DemoAppSharedModule.cs | 9 +-- .../Jobs/SampleJobCreator.cs | 27 ++++---- .../background-jobs/app/docker-compose.yml | 25 +++++++ 31 files changed, 334 insertions(+), 247 deletions(-) create mode 100644 .cursor/hooks/state/continual-learning-index.json create mode 100644 .cursor/hooks/state/continual-learning.json create mode 100644 framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobArgs.cs create mode 100644 framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobHandlerRegistry.cs delete mode 100644 framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobContext.cs delete mode 100644 framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobHandlerProvider.cs create mode 100644 framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IAnonymousJobHandlerRegistry.cs delete mode 100644 framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IDynamicBackgroundJobHandlerProvider.cs create mode 100644 framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AnonymousJobExecutorAsyncBackgroundJob.cs create mode 100644 framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/AnonymousJobExecutionTracker.cs delete mode 100644 framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/DynamicJobExecutionTracker.cs create mode 100644 modules/background-jobs/app/docker-compose.yml diff --git a/.cursor/hooks/state/continual-learning-index.json b/.cursor/hooks/state/continual-learning-index.json new file mode 100644 index 0000000000..43a36a8c73 --- /dev/null +++ b/.cursor/hooks/state/continual-learning-index.json @@ -0,0 +1,9 @@ +{ + "version": 1, + "transcripts": { + "C:\\Users\\salih\\.cursor\\projects\\d-GitHub2-abp\\agent-transcripts\\ded34ec4-ff99-404a-8b16-10baf3c5d9f5\\ded34ec4-ff99-404a-8b16-10baf3c5d9f5.jsonl": { + "mtimeMs": 1773090599259, + "lastProcessedAt": "2026-03-10T00:18:00.000Z" + } + } +} diff --git a/.cursor/hooks/state/continual-learning.json b/.cursor/hooks/state/continual-learning.json new file mode 100644 index 0000000000..a24cb999b0 --- /dev/null +++ b/.cursor/hooks/state/continual-learning.json @@ -0,0 +1,8 @@ +{ + "version": 1, + "lastRunAtMs": 1773090515725, + "turnsSinceLastRun": 5, + "lastTranscriptMtimeMs": 1773090515368, + "lastProcessedGenerationId": "b1b803a9-44bb-4047-a116-4c108a6dadd1", + "trialStartedAtMs": null +} diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs index 10cacce13e..b10b9cec0e 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; +using System.Threading; using System.Threading.Tasks; namespace Volo.Abp.BackgroundJobs; @@ -10,6 +11,7 @@ public class AbpBackgroundJobOptions { private readonly Dictionary _jobConfigurationsByArgsType; private readonly ConcurrentDictionary _jobConfigurationsByName; + private readonly ConcurrentDictionary> _anonymousHandlers = new(); /// /// Default: true. @@ -60,7 +62,7 @@ public class AbpBackgroundJobOptions public BackgroundJobConfiguration? GetJobOrNull(string name) { - return _jobConfigurationsByName.GetValueOrDefault(name); + return _jobConfigurationsByName.TryGetValue(name, out var config) ? config : null; } public IReadOnlyList GetJobs() @@ -84,28 +86,30 @@ public class AbpBackgroundJobOptions _jobConfigurationsByName[jobConfiguration.JobName] = jobConfiguration; } - public void AddDynamicJob(string jobName, Func handler) + public void AddAnonymousJobHandler(string jobName, Func handler) { - var config = new BackgroundJobConfiguration(jobName, handler); - _jobConfigurationsByName[jobName] = config; + Check.NotNullOrWhiteSpace(jobName, nameof(jobName)); + Check.NotNull(handler, nameof(handler)); + + _anonymousHandlers[jobName] = handler; } - public void AddDynamicJob(string jobName, Action handler) + public void AddAnonymousJobHandler(string jobName, Action handler) { - AddDynamicJob(jobName, context => + AddAnonymousJobHandler(jobName, (jsonData, sp, ct) => { - handler(context); + handler(jsonData, sp, ct); return Task.CompletedTask; }); } - public bool RemoveDynamicJob(string name) + internal bool TryGetAnonymousHandler(string jobName, out Func? handler) { - if (_jobConfigurationsByName.TryGetValue(name, out var config) && config.IsDynamic) - { - return _jobConfigurationsByName.TryRemove(name, out _); - } + return _anonymousHandlers.TryGetValue(jobName, out handler); + } - return false; + internal bool IsAnonymousJobRegistered(string jobName) + { + return _anonymousHandlers.ContainsKey(jobName); } } diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobArgs.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobArgs.cs new file mode 100644 index 0000000000..09bac99878 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobArgs.cs @@ -0,0 +1,17 @@ +namespace Volo.Abp.BackgroundJobs; + +[BackgroundJobName("AnonymousJob")] +public class AnonymousJobArgs +{ + public const string JobNameConstant = "AnonymousJob"; + + public string JobName { get; } + + public string JsonData { get; } + + public AnonymousJobArgs(string jobName, string jsonData) + { + JobName = Check.NotNullOrWhiteSpace(jobName, nameof(jobName)); + JsonData = Check.NotNull(jsonData, nameof(jsonData)); + } +} diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobHandlerRegistry.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobHandlerRegistry.cs new file mode 100644 index 0000000000..66c8f95ec2 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobHandlerRegistry.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BackgroundJobs; + +public class AnonymousJobHandlerRegistry : IAnonymousJobHandlerRegistry, ISingletonDependency +{ + private readonly ConcurrentDictionary> _handlers = new(); + private readonly AbpBackgroundJobOptions _options; + + public AnonymousJobHandlerRegistry(IOptions options) + { + _options = options.Value; + } + + public virtual void Register(string jobName, Func handler) + { + Check.NotNullOrWhiteSpace(jobName, nameof(jobName)); + Check.NotNull(handler, nameof(handler)); + + _handlers[jobName] = handler; + } + + public virtual void Register(string jobName, Action handler) + { + Register(jobName, (jsonData, sp, ct) => + { + handler(jsonData, sp, ct); + return Task.CompletedTask; + }); + } + + public virtual bool Unregister(string jobName) + { + return _handlers.TryRemove(jobName, out _); + } + + public virtual bool IsRegistered(string jobName) + { + return _handlers.ContainsKey(jobName) || _options.IsAnonymousJobRegistered(jobName); + } + + public virtual Func? Get(string jobName) + { + if (_handlers.TryGetValue(jobName, out var handler)) + { + return handler; + } + + return _options.TryGetAnonymousHandler(jobName, out handler) ? handler : null; + } +} diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobConfiguration.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobConfiguration.cs index cd0e844a99..fa54ab5538 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobConfiguration.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobConfiguration.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Volo.Abp.BackgroundJobs; @@ -8,29 +6,14 @@ public class BackgroundJobConfiguration { public Type ArgsType { get; } - public Type? JobType { get; } + public Type JobType { get; } public string JobName { get; } - public bool IsDynamic { get; } - - public Func? DynamicHandler { get; } - public BackgroundJobConfiguration(Type jobType, string jobName) { JobType = jobType; ArgsType = BackgroundJobArgsHelper.GetJobArgsType(jobType); JobName = jobName; } - - public BackgroundJobConfiguration(string jobName, Func handler) - { - Check.NotNullOrWhiteSpace(jobName, nameof(jobName)); - Check.NotNull(handler, nameof(handler)); - - JobName = jobName; - DynamicHandler = handler; - IsDynamic = true; - ArgsType = typeof(Dictionary); - } } diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobExecuter.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobExecuter.cs index d84845ee0f..0170b881df 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobExecuter.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobExecuter.cs @@ -2,8 +2,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using System; -using System.Collections.Generic; -using System.Text.Json; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; @@ -31,54 +29,9 @@ public class BackgroundJobExecuter : IBackgroundJobExecuter, ITransientDependenc public virtual async Task ExecuteAsync(JobExecutionContext context) { - if (context.JobName != null) - { - var jobConfig = Options.GetJobOrNull(context.JobName); - if (jobConfig?.DynamicHandler != null) - { - await ExecuteDynamicHandlerAsync(context, jobConfig); - return; - } - } - await ExecuteTypedHandlerAsync(context); } - protected virtual async Task ExecuteDynamicHandlerAsync(JobExecutionContext context, BackgroundJobConfiguration jobConfig) - { - try - { - var cancellationTokenProvider = - context.ServiceProvider.GetRequiredService(); - - using (cancellationTokenProvider.Use(context.CancellationToken)) - { - var dictArgs = EnsureDictionaryArgs(context.JobArgs); - var dynamicContext = new DynamicBackgroundJobContext( - context.ServiceProvider, - dictArgs, - context.CancellationToken - ); - - await jobConfig.DynamicHandler!(dynamicContext); - } - } - catch (Exception ex) - { - Logger.LogException(ex); - - await context.ServiceProvider - .GetRequiredService() - .NotifyAsync(new ExceptionNotificationContext(ex)); - - throw new BackgroundJobExecutionException("A background job execution is failed. See inner exception for details.", ex) - { - JobType = context.JobName!, - JobArgs = context.JobArgs - }; - } - } - protected virtual async Task ExecuteTypedHandlerAsync(JobExecutionContext context) { var job = context.ServiceProvider.GetService(context.JobType); @@ -131,24 +84,6 @@ public class BackgroundJobExecuter : IBackgroundJobExecuter, ITransientDependenc } } - protected virtual Dictionary EnsureDictionaryArgs(object jobArgs) - { - if (jobArgs is Dictionary dict) - { - return dict; - } - - if (jobArgs is JsonElement jsonElement) - { - return JsonSerializer.Deserialize>(jsonElement.GetRawText()) - ?? new Dictionary(); - } - - var json = JsonSerializer.Serialize(jobArgs); - return JsonSerializer.Deserialize>(json) - ?? new Dictionary(); - } - protected virtual Guid? GetJobArgsTenantId(object jobArgs) { return jobArgs switch diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobContext.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobContext.cs deleted file mode 100644 index 49ffe89aca..0000000000 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobContext.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.BackgroundJobs; - -public class DynamicBackgroundJobContext : IServiceProviderAccessor -{ - public IServiceProvider ServiceProvider { get; } - - public Dictionary Args { get; } - - public CancellationToken CancellationToken { get; } - - public DynamicBackgroundJobContext( - IServiceProvider serviceProvider, - Dictionary args, - CancellationToken cancellationToken = default) - { - ServiceProvider = serviceProvider; - Args = args; - CancellationToken = cancellationToken; - } -} diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobHandlerProvider.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobHandlerProvider.cs deleted file mode 100644 index 2665719c2a..0000000000 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobHandlerProvider.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.Extensions.Options; -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.BackgroundJobs; - -public class DynamicBackgroundJobHandlerProvider : IDynamicBackgroundJobHandlerProvider, ISingletonDependency -{ - protected AbpBackgroundJobOptions Options { get; } - - public DynamicBackgroundJobHandlerProvider(IOptions options) - { - Options = options.Value; - } - - public virtual void Register(string jobName, Func handler) - { - Options.AddDynamicJob(jobName, handler); - } - - public virtual void Register(string jobName, Action handler) - { - Options.AddDynamicJob(jobName, handler); - } - - public virtual bool Unregister(string jobName) - { - return Options.RemoveDynamicJob(jobName); - } - - public virtual bool IsRegistered(string jobName) - { - return Options.GetJobOrNull(jobName)?.IsDynamic == true; - } -} diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IAnonymousJobHandlerRegistry.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IAnonymousJobHandlerRegistry.cs new file mode 100644 index 0000000000..e02c09ab28 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IAnonymousJobHandlerRegistry.cs @@ -0,0 +1,18 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; + +namespace Volo.Abp.BackgroundJobs; + +public interface IAnonymousJobHandlerRegistry +{ + void Register(string jobName, Func handler); + + void Register(string jobName, Action handler); + + bool Unregister(string jobName); + + bool IsRegistered(string jobName); + + Func? Get(string jobName); +} diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IDynamicBackgroundJobHandlerProvider.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IDynamicBackgroundJobHandlerProvider.cs deleted file mode 100644 index 0f1bb8449a..0000000000 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IDynamicBackgroundJobHandlerProvider.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Volo.Abp.BackgroundJobs; - -public interface IDynamicBackgroundJobHandlerProvider -{ - void Register(string jobName, Func handler); - - void Register(string jobName, Action handler); - - bool Unregister(string jobName); - - bool IsRegistered(string jobName); -} diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/JobExecutionContext.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/JobExecutionContext.cs index a68736bb7d..4ef2d2c19d 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/JobExecutionContext.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/JobExecutionContext.cs @@ -14,19 +14,15 @@ public class JobExecutionContext : IServiceProviderAccessor public CancellationToken CancellationToken { get; } - public string? JobName { get; } - public JobExecutionContext( IServiceProvider serviceProvider, Type jobType, object jobArgs, - CancellationToken cancellationToken = default, - string? jobName = null) + CancellationToken cancellationToken = default) { ServiceProvider = serviceProvider; JobType = jobType; JobArgs = jobArgs; CancellationToken = cancellationToken; - JobName = jobName; } } diff --git a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs index 840b546783..fbd1034ecd 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs @@ -16,15 +16,18 @@ public class HangfireBackgroundJobManager : IBackgroundJobManager, ITransientDep protected IOptions BackgroundJobOptions { get; } protected IOptions HangfireOptions { get; } protected IJsonSerializer JsonSerializer { get; } + protected IAnonymousJobHandlerRegistry AnonymousJobHandlerRegistry { get; } public HangfireBackgroundJobManager( IOptions backgroundJobOptions, IOptions hangfireOptions, - IJsonSerializer jsonSerializer) + IJsonSerializer jsonSerializer, + IAnonymousJobHandlerRegistry anonymousJobHandlerRegistry) { BackgroundJobOptions = backgroundJobOptions; HangfireOptions = hangfireOptions; JsonSerializer = jsonSerializer; + AnonymousJobHandlerRegistry = anonymousJobHandlerRegistry; } public virtual Task EnqueueAsync(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, @@ -43,6 +46,13 @@ public class HangfireBackgroundJobManager : IBackgroundJobManager, ITransientDep public virtual Task EnqueueAsync(string jobName, object args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) { + if (ShouldWrapAsAnonymousJob(jobName)) + { + var jsonData = JsonSerializer.Serialize(args); + var anonymousArgs = new AnonymousJobArgs(jobName, jsonData); + return EnqueueAsync(AnonymousJobArgs.JobNameConstant, anonymousArgs, priority, delay); + } + var serializedArgs = JsonSerializer.Serialize(args); var queueName = GetQueueName(jobName); @@ -56,6 +66,11 @@ public class HangfireBackgroundJobManager : IBackgroundJobManager, ITransientDep )); } + protected virtual bool ShouldWrapAsAnonymousJob(string jobName) + { + return jobName != AnonymousJobArgs.JobNameConstant && AnonymousJobHandlerRegistry.IsRegistered(jobName); + } + protected virtual string GetQueueName(Type argsType) { return GetQueueName(BackgroundJobOptions.Value.GetJob(argsType)); diff --git a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs index b85154a5c3..38d6bbdf52 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs @@ -40,7 +40,7 @@ public class HangfireJobExecutionAdapter using (var scope = ServiceScopeFactory.CreateScope()) { var jobConfiguration = Options.GetJob(typeof(TArgs)); - var context = new JobExecutionContext(scope.ServiceProvider, jobConfiguration.JobType!, args!, cancellationToken: cancellationToken, jobName: jobConfiguration.JobName); + var context = new JobExecutionContext(scope.ServiceProvider, jobConfiguration.JobType!, args!, cancellationToken: cancellationToken); await JobExecuter.ExecuteAsync(context); } } @@ -83,7 +83,7 @@ public class HangfireJobExecutionAdapter { var jobConfiguration = Options.GetJob(jobName); var args = JsonSerializer.Deserialize(jobConfiguration.ArgsType, serializedArgs); - var context = new JobExecutionContext(scope.ServiceProvider, jobConfiguration.JobType ?? typeof(object), args, cancellationToken: cancellationToken, jobName: jobName); + var context = new JobExecutionContext(scope.ServiceProvider, jobConfiguration.JobType, args, cancellationToken: cancellationToken); await JobExecuter.ExecuteAsync(context); } } diff --git a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs index dd8e385246..842f47c78f 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs @@ -19,11 +19,14 @@ public class QuartzBackgroundJobManager : IBackgroundJobManager, ITransientDepen protected IJsonSerializer JsonSerializer { get; } - public QuartzBackgroundJobManager(IScheduler scheduler, IOptions options, IJsonSerializer jsonSerializer) + protected IAnonymousJobHandlerRegistry AnonymousJobHandlerRegistry { get; } + + public QuartzBackgroundJobManager(IScheduler scheduler, IOptions options, IJsonSerializer jsonSerializer, IAnonymousJobHandlerRegistry anonymousJobHandlerRegistry) { Scheduler = scheduler; JsonSerializer = jsonSerializer; Options = options.Value; + AnonymousJobHandlerRegistry = anonymousJobHandlerRegistry; } public virtual async Task EnqueueAsync(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, @@ -35,9 +38,21 @@ public class QuartzBackgroundJobManager : IBackgroundJobManager, ITransientDepen public virtual async Task EnqueueAsync(string jobName, object args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) { + if (ShouldWrapAsAnonymousJob(jobName)) + { + var jsonData = JsonSerializer.Serialize(args); + var anonymousArgs = new AnonymousJobArgs(jobName, jsonData); + return await EnqueueAsync(AnonymousJobArgs.JobNameConstant, anonymousArgs, priority, delay); + } + return await ReEnqueueAsync(jobName, args, Options.RetryCount, Options.RetryIntervalMillisecond, priority, delay); } + protected virtual bool ShouldWrapAsAnonymousJob(string jobName) + { + return jobName != AnonymousJobArgs.JobNameConstant && AnonymousJobHandlerRegistry.IsRegistered(jobName); + } + public virtual async Task ReEnqueueAsync(TArgs args, int retryCount, int retryIntervalMillisecond, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) { diff --git a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs index defdb87031..d6e690dcff 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs @@ -97,7 +97,7 @@ public class QuartzJobExecutionAdapter : IJob var serializedArgs = context.JobDetail.JobDataMap.GetString(JobArgsKey)!; var jobConfiguration = Options.GetJob(jobName); var args = JsonSerializer.Deserialize(jobConfiguration.ArgsType, serializedArgs); - var jobContext = new JobExecutionContext(scope.ServiceProvider, jobConfiguration.JobType ?? typeof(object), args, cancellationToken: context.CancellationToken, jobName: jobName); + var jobContext = new JobExecutionContext(scope.ServiceProvider, jobConfiguration.JobType, args, cancellationToken: context.CancellationToken); try { await JobExecuter.ExecuteAsync(jobContext); diff --git a/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueue.cs b/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueue.cs index be7614060d..33c41704f2 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueue.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueue.cs @@ -214,9 +214,8 @@ public class JobQueue : IJobQueue { var context = new JobExecutionContext( scope.ServiceProvider, - JobConfiguration.JobType ?? typeof(object), - Serializer.Deserialize(ea.Body.ToArray(), typeof(TArgs)), - jobName: JobConfiguration.JobName + JobConfiguration.JobType, + Serializer.Deserialize(ea.Body.ToArray(), typeof(TArgs)) ); try diff --git a/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/RabbitMqBackgroundJobManager.cs b/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/RabbitMqBackgroundJobManager.cs index 69ac3c0a62..5c7b420c98 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/RabbitMqBackgroundJobManager.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/RabbitMqBackgroundJobManager.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; +using Volo.Abp.Json; namespace Volo.Abp.BackgroundJobs.RabbitMQ; @@ -8,10 +9,14 @@ namespace Volo.Abp.BackgroundJobs.RabbitMQ; public class RabbitMqBackgroundJobManager : IBackgroundJobManager, ITransientDependency { protected IJobQueueManager JobQueueManager { get; } + protected IAnonymousJobHandlerRegistry AnonymousJobHandlerRegistry { get; } + protected IJsonSerializer JsonSerializer { get; } - public RabbitMqBackgroundJobManager(IJobQueueManager jobQueueManager) + public RabbitMqBackgroundJobManager(IJobQueueManager jobQueueManager, IAnonymousJobHandlerRegistry anonymousJobHandlerRegistry, IJsonSerializer jsonSerializer) { JobQueueManager = jobQueueManager; + AnonymousJobHandlerRegistry = anonymousJobHandlerRegistry; + JsonSerializer = jsonSerializer; } public virtual async Task EnqueueAsync( @@ -29,7 +34,19 @@ public class RabbitMqBackgroundJobManager : IBackgroundJobManager, ITransientDep BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) { + if (ShouldWrapAsAnonymousJob(jobName)) + { + var jsonData = JsonSerializer.Serialize(args); + var anonymousArgs = new AnonymousJobArgs(jobName, jsonData); + return await EnqueueAsync(AnonymousJobArgs.JobNameConstant, anonymousArgs, priority, delay); + } + var jobQueue = await JobQueueManager.GetAsync(jobName); return (await jobQueue.EnqueueAsync(args, priority, delay))!; } + + protected virtual bool ShouldWrapAsAnonymousJob(string jobName) + { + return jobName != AnonymousJobArgs.JobNameConstant && AnonymousJobHandlerRegistry.IsRegistered(jobName); + } } diff --git a/framework/src/Volo.Abp.BackgroundJobs.TickerQ/Volo/Abp/BackgroundJobs/TickerQ/AbpBackgroundJobsTickerQModule.cs b/framework/src/Volo.Abp.BackgroundJobs.TickerQ/Volo/Abp/BackgroundJobs/TickerQ/AbpBackgroundJobsTickerQModule.cs index 915e061057..1cde94b517 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.TickerQ/Volo/Abp/BackgroundJobs/TickerQ/AbpBackgroundJobsTickerQModule.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.TickerQ/Volo/Abp/BackgroundJobs/TickerQ/AbpBackgroundJobsTickerQModule.cs @@ -67,7 +67,7 @@ public class AbpBackgroundJobsTickerQModule : AbpModule var jobExecuter = serviceProvider.GetRequiredService(); var args = await TickerRequestProvider.GetRequestAsync(context, cancellationToken); var jobConfiguration = options.GetJob(typeof(TArgs)); - var jobExecutionContext = new JobExecutionContext(scope.ServiceProvider, jobConfiguration.JobType ?? typeof(object), args!, cancellationToken: cancellationToken, jobName: jobConfiguration.JobName); + var jobExecutionContext = new JobExecutionContext(scope.ServiceProvider, jobConfiguration.JobType, args!, cancellationToken: cancellationToken); await jobExecuter.ExecuteAsync(jobExecutionContext); } }; diff --git a/framework/src/Volo.Abp.BackgroundJobs.TickerQ/Volo/Abp/BackgroundJobs/TickerQ/AbpTickerQBackgroundJobManager.cs b/framework/src/Volo.Abp.BackgroundJobs.TickerQ/Volo/Abp/BackgroundJobs/TickerQ/AbpTickerQBackgroundJobManager.cs index f70e48fcf5..0b4557f09c 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.TickerQ/Volo/Abp/BackgroundJobs/TickerQ/AbpTickerQBackgroundJobManager.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.TickerQ/Volo/Abp/BackgroundJobs/TickerQ/AbpTickerQBackgroundJobManager.cs @@ -6,6 +6,7 @@ using TickerQ.Utilities; using TickerQ.Utilities.Entities; using TickerQ.Utilities.Interfaces.Managers; using Volo.Abp.DependencyInjection; +using Volo.Abp.Json; namespace Volo.Abp.BackgroundJobs.TickerQ; @@ -17,15 +18,21 @@ public class AbpTickerQBackgroundJobManager : IBackgroundJobManager, ITransientD protected ITimeTickerManager TimeTickerManager { get; } protected AbpBackgroundJobOptions Options { get; } protected AbpBackgroundJobsTickerQOptions TickerQOptions { get; } + protected IAnonymousJobHandlerRegistry AnonymousJobHandlerRegistry { get; } + protected IJsonSerializer JsonSerializer { get; } public AbpTickerQBackgroundJobManager( ITimeTickerManager timeTickerManager, IOptions options, - IOptions tickerQOptions) + IOptions tickerQOptions, + IAnonymousJobHandlerRegistry anonymousJobHandlerRegistry, + IJsonSerializer jsonSerializer) { TimeTickerManager = timeTickerManager; Options = options.Value; TickerQOptions = tickerQOptions.Value; + AnonymousJobHandlerRegistry = anonymousJobHandlerRegistry; + JsonSerializer = jsonSerializer; } public virtual async Task EnqueueAsync(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) @@ -36,10 +43,22 @@ public class AbpTickerQBackgroundJobManager : IBackgroundJobManager, ITransientD public virtual async Task EnqueueAsync(string jobName, object args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) { + if (ShouldWrapAsAnonymousJob(jobName)) + { + var jsonData = JsonSerializer.Serialize(args); + var anonymousArgs = new AnonymousJobArgs(jobName, jsonData); + return await EnqueueAsync(AnonymousJobArgs.JobNameConstant, anonymousArgs, priority, delay); + } + var job = Options.GetJob(jobName); return await EnqueueAsync(job, args, priority, delay); } + protected virtual bool ShouldWrapAsAnonymousJob(string jobName) + { + return jobName != AnonymousJobArgs.JobNameConstant && AnonymousJobHandlerRegistry.IsRegistered(jobName); + } + protected virtual async Task EnqueueAsync(BackgroundJobConfiguration job, object args, BackgroundJobPriority priority, TimeSpan? delay) { var timeTicker = new TimeTickerEntity diff --git a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AnonymousJobExecutorAsyncBackgroundJob.cs b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AnonymousJobExecutorAsyncBackgroundJob.cs new file mode 100644 index 0000000000..6bc0c594c7 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AnonymousJobExecutorAsyncBackgroundJob.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; + +namespace Volo.Abp.BackgroundJobs; + +public class AnonymousJobExecutorAsyncBackgroundJob : AsyncBackgroundJob, ITransientDependency +{ + protected IAnonymousJobHandlerRegistry HandlerRegistry { get; } + protected IServiceProvider ServiceProvider { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + + public AnonymousJobExecutorAsyncBackgroundJob( + IAnonymousJobHandlerRegistry handlerRegistry, + IServiceProvider serviceProvider, + ICancellationTokenProvider cancellationTokenProvider) + { + HandlerRegistry = handlerRegistry; + ServiceProvider = serviceProvider; + CancellationTokenProvider = cancellationTokenProvider; + } + + public override async Task ExecuteAsync(AnonymousJobArgs args) + { + var handler = HandlerRegistry.Get(args.JobName); + if (handler == null) + { + throw new AbpException("No anonymous job handler registered for: " + args.JobName); + } + + await handler(args.JsonData, ServiceProvider, CancellationTokenProvider.Token); + } +} diff --git a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs index 930beecd5c..159f11a6d3 100644 --- a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs +++ b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs @@ -65,10 +65,9 @@ public class BackgroundJobWorker : AsyncPeriodicBackgroundWorkerBase, IBackgroun var jobArgs = serializer.Deserialize(jobInfo.JobArgs, jobConfiguration.ArgsType); var context = new JobExecutionContext( workerContext.ServiceProvider, - jobConfiguration.JobType ?? typeof(object), + jobConfiguration.JobType, jobArgs, - workerContext.CancellationToken, - jobName: jobInfo.JobName); + workerContext.CancellationToken); try { diff --git a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/DefaultBackgroundJobManager.cs b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/DefaultBackgroundJobManager.cs index 582d351431..1760342ef1 100644 --- a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/DefaultBackgroundJobManager.cs +++ b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/DefaultBackgroundJobManager.cs @@ -18,6 +18,7 @@ public class DefaultBackgroundJobManager : IBackgroundJobManager, ITransientDepe protected IBackgroundJobSerializer Serializer { get; } protected IGuidGenerator GuidGenerator { get; } protected IBackgroundJobStore Store { get; } + protected IAnonymousJobHandlerRegistry AnonymousJobHandlerRegistry { get; } protected IOptions BackgroundJobOptions { get; } protected IOptions BackgroundJobWorkerOptions { get; } @@ -26,12 +27,14 @@ public class DefaultBackgroundJobManager : IBackgroundJobManager, ITransientDepe IBackgroundJobSerializer serializer, IBackgroundJobStore store, IGuidGenerator guidGenerator, + IAnonymousJobHandlerRegistry anonymousJobHandlerRegistry, IOptions backgroundJobOptions, IOptions backgroundJobWorkerOptions) { Clock = clock; Serializer = serializer; GuidGenerator = guidGenerator; + AnonymousJobHandlerRegistry = anonymousJobHandlerRegistry; BackgroundJobOptions = backgroundJobOptions; BackgroundJobWorkerOptions = backgroundJobWorkerOptions; Store = store; @@ -45,6 +48,13 @@ public class DefaultBackgroundJobManager : IBackgroundJobManager, ITransientDepe public virtual async Task EnqueueAsync(string jobName, object args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) { + if (ShouldWrapAsAnonymousJob(jobName)) + { + var jsonData = Serializer.Serialize(args); + var anonymousArgs = new AnonymousJobArgs(jobName, jsonData); + return await EnqueueAsync(AnonymousJobArgs.JobNameConstant, anonymousArgs, priority, delay); + } + var jobInfo = new BackgroundJobInfo { Id = GuidGenerator.Create(), @@ -65,4 +75,9 @@ public class DefaultBackgroundJobManager : IBackgroundJobManager, ITransientDepe return jobInfo.Id.ToString(); } + + protected virtual bool ShouldWrapAsAnonymousJob(string jobName) + { + return jobName != AnonymousJobArgs.JobNameConstant && AnonymousJobHandlerRegistry.IsRegistered(jobName); + } } diff --git a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/AbpBackgroundJobsTestModule.cs b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/AbpBackgroundJobsTestModule.cs index 14e2c558bd..ee792812ac 100644 --- a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/AbpBackgroundJobsTestModule.cs +++ b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/AbpBackgroundJobsTestModule.cs @@ -13,14 +13,14 @@ public class AbpBackgroundJobsTestModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.AddSingleton(); + context.Services.AddSingleton(); Configure(options => { - options.AddDynamicJob("TestDynamicJob", context => + options.AddAnonymousJobHandler("TestAnonymousJob", (jsonData, sp, ct) => { - var tracker = context.ServiceProvider.GetRequiredService(); - tracker.ExecutedArgs.Add(context.Args); + var tracker = sp.GetRequiredService(); + tracker.ExecutedJsonData.Add(jsonData); return System.Threading.Tasks.Task.CompletedTask; }); }); diff --git a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/AnonymousJobExecutionTracker.cs b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/AnonymousJobExecutionTracker.cs new file mode 100644 index 0000000000..b3160a2829 --- /dev/null +++ b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/AnonymousJobExecutionTracker.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Volo.Abp.BackgroundJobs; + +public class AnonymousJobExecutionTracker +{ + public List ExecutedJsonData { get; } = new(); +} diff --git a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobExecuter_Tests.cs b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobExecuter_Tests.cs index 87f7ff316b..a46e79d15d 100644 --- a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobExecuter_Tests.cs +++ b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobExecuter_Tests.cs @@ -139,54 +139,52 @@ public class BackgroundJobExecuter_Tests : BackgroundJobsTestBase } [Fact] - public async Task Should_Execute_Dynamic_Handler() + public async Task Should_Execute_Anonymous_Job_Handler() { - var tracker = GetRequiredService(); - tracker.ExecutedArgs.ShouldBeEmpty(); + var tracker = GetRequiredService(); + tracker.ExecutedJsonData.ShouldBeEmpty(); - var args = new Dictionary { ["Value"] = "dynamic-42" }; + var args = new AnonymousJobArgs("TestAnonymousJob", "{\"OrderId\":\"ORD-001\"}"); await _backgroundJobExecuter.ExecuteAsync( new JobExecutionContext( ServiceProvider, - typeof(object), - args, - jobName: "TestDynamicJob" + typeof(AnonymousJobExecutorAsyncBackgroundJob), + args ) ); - tracker.ExecutedArgs.Count.ShouldBe(1); - tracker.ExecutedArgs[0]["Value"].ShouldBe("dynamic-42"); + tracker.ExecutedJsonData.Count.ShouldBe(1); + tracker.ExecutedJsonData[0].ShouldContain("ORD-001"); } [Fact] - public async Task Should_Execute_Dynamic_Handler_Registered_At_Runtime() + public async Task Should_Execute_Anonymous_Job_Handler_Registered_At_Runtime() { - var handlerProvider = GetRequiredService(); + var handlerRegistry = GetRequiredService(); var executedValues = new List(); - handlerProvider.Register("RuntimeDynamicJob", context => + handlerRegistry.Register("RuntimeAnonymousJob", (jsonData, sp, ct) => { - executedValues.Add(context.Args["Message"]?.ToString()!); + executedValues.Add(jsonData); return Task.CompletedTask; }); - var args = new Dictionary { ["Message"] = "hello-runtime" }; + var args = new AnonymousJobArgs("RuntimeAnonymousJob", "{\"Message\":\"hello-runtime\"}"); await _backgroundJobExecuter.ExecuteAsync( new JobExecutionContext( ServiceProvider, - typeof(object), - args, - jobName: "RuntimeDynamicJob" + typeof(AnonymousJobExecutorAsyncBackgroundJob), + args ) ); executedValues.Count.ShouldBe(1); - executedValues[0].ShouldBe("hello-runtime"); + executedValues[0].ShouldContain("hello-runtime"); - handlerProvider.IsRegistered("RuntimeDynamicJob").ShouldBeTrue(); - handlerProvider.Unregister("RuntimeDynamicJob").ShouldBeTrue(); - handlerProvider.IsRegistered("RuntimeDynamicJob").ShouldBeFalse(); + handlerRegistry.IsRegistered("RuntimeAnonymousJob").ShouldBeTrue(); + handlerRegistry.Unregister("RuntimeAnonymousJob").ShouldBeTrue(); + handlerRegistry.IsRegistered("RuntimeAnonymousJob").ShouldBeFalse(); } } diff --git a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobManager_Tests.cs b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobManager_Tests.cs index 75d1218f94..bb8d05bf86 100644 --- a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobManager_Tests.cs +++ b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobManager_Tests.cs @@ -64,17 +64,15 @@ public class BackgroundJobManager_Tests : BackgroundJobsTestBase } [Fact] - public async Task Should_Store_Dynamic_Jobs() + public async Task Should_Store_Anonymous_Jobs() { - var jobIdAsString = await _backgroundJobManager.EnqueueAsync("TestDynamicJob", (object)new Dictionary - { - ["OrderId"] = "ORD-001" - }); + var jobIdAsString = await _backgroundJobManager.EnqueueAsync("TestAnonymousJob", new { OrderId = "ORD-001" }); jobIdAsString.ShouldNotBe(default); var jobInfo = await _backgroundJobStore.FindAsync(Guid.Parse(jobIdAsString)); jobInfo.ShouldNotBeNull(); - jobInfo.JobName.ShouldBe("TestDynamicJob"); + jobInfo.JobName.ShouldBe(AnonymousJobArgs.JobNameConstant); + jobInfo.JobArgs.ShouldContain("TestAnonymousJob"); jobInfo.JobArgs.ShouldContain("ORD-001"); } } diff --git a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/DynamicJobExecutionTracker.cs b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/DynamicJobExecutionTracker.cs deleted file mode 100644 index dec387ab97..0000000000 --- a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/DynamicJobExecutionTracker.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Collections.Generic; - -namespace Volo.Abp.BackgroundJobs; - -public class DynamicJobExecutionTracker -{ - public List> ExecutedArgs { get; } = new(); -} diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/DemoAppSharedModule.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/DemoAppSharedModule.cs index 47da99d683..58046b6313 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/DemoAppSharedModule.cs +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/DemoAppSharedModule.cs @@ -1,4 +1,5 @@ using System; +using System.Text.Json; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs; @@ -14,11 +15,11 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared { Configure(options => { - options.AddDynamicJob("CompileTimeDynamicJob", dynamicContext => + options.AddAnonymousJobHandler("CompileTimeAnonymousJob", (jsonData, sp, ct) => { - dynamicContext.Args.TryGetValue("Value", out var valueObj); - var value = valueObj?.ToString(); - Console.WriteLine($"[DYNAMIC-COMPILE] {value}"); + var doc = JsonDocument.Parse(jsonData); + var value = doc.RootElement.TryGetProperty("Value", out var prop) ? prop.GetString() : null; + Console.WriteLine($"[ANONYMOUS-COMPILE] {value}"); return Task.CompletedTask; }); }); diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/SampleJobCreator.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/SampleJobCreator.cs index a9ceca9bdd..485468582b 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/SampleJobCreator.cs +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/SampleJobCreator.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using System.Text.Json; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Threading; @@ -9,14 +9,14 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs public class SampleJobCreator : ITransientDependency { private readonly IBackgroundJobManager _backgroundJobManager; - private readonly IDynamicBackgroundJobHandlerProvider _dynamicBackgroundJobHandlerProvider; + private readonly IAnonymousJobHandlerRegistry _anonymousJobHandlerRegistry; public SampleJobCreator( IBackgroundJobManager backgroundJobManager, - IDynamicBackgroundJobHandlerProvider dynamicBackgroundJobHandlerProvider) + IAnonymousJobHandlerRegistry anonymousJobHandlerRegistry) { _backgroundJobManager = backgroundJobManager; - _dynamicBackgroundJobHandlerProvider = dynamicBackgroundJobHandlerProvider; + _anonymousJobHandlerRegistry = anonymousJobHandlerRegistry; } public void CreateJobs() @@ -26,10 +26,11 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs public async Task CreateJobsAsync() { - _dynamicBackgroundJobHandlerProvider.Register("RuntimeDynamicJob", context => + _anonymousJobHandlerRegistry.Register("RuntimeAnonymousJob", (jsonData, sp, ct) => { - context.Args.TryGetValue("Value", out var valueObj); - Console.WriteLine($"[DYNAMIC-RUNTIME] {valueObj}"); + var doc = JsonDocument.Parse(jsonData); + var value = doc.RootElement.TryGetProperty("Value", out var prop) ? prop.GetString() : null; + Console.WriteLine($"[ANONYMOUS-RUNTIME] {value}"); return Task.CompletedTask; }); @@ -57,16 +58,16 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs (object)new { Value = "test 3 (yellow) - by name, anonymous", Time = DateTime.Now } ); - // Dynamic enqueue (compile-time and runtime handlers) - if (!_backgroundJobManager.GetType().Name.Contains("RabbitMq", StringComparison.OrdinalIgnoreCase)) + // Anonymous job enqueue (compile-time and runtime handlers) + if (!_backgroundJobManager.GetType().Name.ToUpperInvariant().Contains("RABBITMQ")) { await _backgroundJobManager.EnqueueAsync( - "CompileTimeDynamicJob", - (object)new Dictionary { ["Value"] = "test 4 (dynamic) - compile-time" } + "CompileTimeAnonymousJob", + new { Value = "test 4 (anonymous) - compile-time" } ); await _backgroundJobManager.EnqueueAsync( - "RuntimeDynamicJob", - (object)new Dictionary { ["Value"] = "test 5 (dynamic) - runtime" } + "RuntimeAnonymousJob", + new { Value = "test 5 (anonymous) - runtime" } ); } } diff --git a/modules/background-jobs/app/docker-compose.yml b/modules/background-jobs/app/docker-compose.yml new file mode 100644 index 0000000000..613711d0b7 --- /dev/null +++ b/modules/background-jobs/app/docker-compose.yml @@ -0,0 +1,25 @@ +services: + sqlserver: + image: mcr.microsoft.com/mssql/server:2022-latest + environment: + ACCEPT_EULA: "Y" + MSSQL_SA_PASSWORD: "YourStrong!Passw0rd" + ports: + - "1433:1433" + volumes: + - sqlserver-data:/var/opt/mssql + + rabbitmq: + image: rabbitmq:4-management + ports: + - "5672:5672" + - "15672:15672" + environment: + RABBITMQ_DEFAULT_USER: "guest" + RABBITMQ_DEFAULT_PASS: "guest" + volumes: + - rabbitmq-data:/var/lib/rabbitmq + +volumes: + sqlserver-data: + rabbitmq-data: