Browse Source

Implement dynamic background job handling with new DynamicBackgroundJobArgs and executor

pull/25059/head
maliming 1 week ago
parent
commit
92a72fcef5
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 2
      docs/en/framework/infrastructure/background-jobs/index.md
  2. 8
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DefaultDynamicBackgroundJobManager.cs
  3. 6
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobArgs.cs
  4. 8
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobExecutorJob.cs
  5. 2
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IDynamicBackgroundJobManager.cs
  6. 4
      framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/AbpDashboardOptionsProvider.cs
  7. 8
      framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobManager_Tests.cs
  8. 4
      modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/DemoAppSharedModule.cs
  9. 16
      modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/SampleJobCreator.cs

2
docs/en/framework/infrastructure/background-jobs/index.md

@ -420,7 +420,7 @@ bool removed = _dynamicJobManager.UnregisterHandler("ProcessOrder");
### How It Works
- **Typed job path**: When the job name matches a registered typed job configuration, the args are serialized to JSON and deserialized to the expected args type, then enqueued through `IBackgroundJobManager.EnqueueAsync<TArgs>`.
- **Dynamic handler path**: When the job name matches a registered dynamic handler, the args are wrapped as `AnonymousJobArgs` (an internal transport type) and enqueued through `IBackgroundJobManager.EnqueueAsync<AnonymousJobArgs>`. When the job executes, the framework looks up the handler by name and invokes it.
- **Dynamic handler path**: When the job name matches a registered dynamic handler, the args are wrapped as `DynamicBackgroundJobArgs` (an internal transport type) and enqueued through `IBackgroundJobManager.EnqueueAsync<DynamicBackgroundJobArgs>`. When the job executes, the framework looks up the handler by name and invokes it.
- All dynamic jobs go through the **standard typed job pipeline**, which means they work with all providers (Default, Hangfire, Quartz, RabbitMQ, TickerQ) without any provider-specific changes.
## Integrations

8
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DefaultDynamicBackgroundJobManager.cs

@ -47,7 +47,7 @@ public class DefaultDynamicBackgroundJobManager : IDynamicBackgroundJobManager,
if (HandlerRegistry.IsRegistered(jobName))
{
return await EnqueueAnonymousJobAsync(jobName, args, priority, delay);
return await EnqueueDynamicHandlerJobAsync(jobName, args, priority, delay);
}
throw new AbpException(
@ -88,15 +88,15 @@ public class DefaultDynamicBackgroundJobManager : IDynamicBackgroundJobManager,
return await task;
}
protected virtual Task<string> EnqueueAnonymousJobAsync(
protected virtual Task<string> EnqueueDynamicHandlerJobAsync(
string jobName,
object args,
BackgroundJobPriority priority,
TimeSpan? delay)
{
var jsonData = JsonSerializer.Serialize(args);
var anonymousArgs = new AnonymousJobArgs(jobName, jsonData);
return BackgroundJobManager.EnqueueAsync(anonymousArgs, priority, delay);
var dynamicArgs = new DynamicBackgroundJobArgs(jobName, jsonData);
return BackgroundJobManager.EnqueueAsync(dynamicArgs, priority, delay);
}
private static MethodInfo GetOrCreateEnqueueMethod(Type argsType)

6
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobArgs.cs → framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobArgs.cs

@ -1,15 +1,15 @@
namespace Volo.Abp.BackgroundJobs;
[BackgroundJobName(JobNameConstant)]
public class AnonymousJobArgs
public class DynamicBackgroundJobArgs
{
public const string JobNameConstant = "Abp.AnonymousJob";
public const string JobNameConstant = "Abp.DynamicJob";
public string JobName { get; }
public string JsonData { get; }
public AnonymousJobArgs(string jobName, string jsonData)
public DynamicBackgroundJobArgs(string jobName, string jsonData)
{
JobName = Check.NotNullOrWhiteSpace(jobName, nameof(jobName));
JsonData = Check.NotNull(jsonData, nameof(jsonData));

8
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobExecutorAsyncBackgroundJob.cs → framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobExecutorJob.cs

@ -7,12 +7,12 @@ using Volo.Abp.Threading;
namespace Volo.Abp.BackgroundJobs;
public class AnonymousJobExecutorAsyncBackgroundJob : AsyncBackgroundJob<AnonymousJobArgs>, ITransientDependency
public class DynamicBackgroundJobExecutorJob : AsyncBackgroundJob<DynamicBackgroundJobArgs>, ITransientDependency
{
protected IDynamicBackgroundJobHandlerRegistry HandlerRegistry { get; }
protected IServiceProvider ServiceProvider { get; }
public AnonymousJobExecutorAsyncBackgroundJob(
public DynamicBackgroundJobExecutorJob(
IDynamicBackgroundJobHandlerRegistry handlerRegistry,
IServiceProvider serviceProvider)
{
@ -20,11 +20,11 @@ public class AnonymousJobExecutorAsyncBackgroundJob : AsyncBackgroundJob<Anonymo
ServiceProvider = serviceProvider;
}
public override async Task ExecuteAsync(AnonymousJobArgs args)
public override async Task ExecuteAsync(DynamicBackgroundJobArgs args)
{
Logger.LogInformation(
"Executing dynamic job. TransportJobName: {TransportJobName}, EffectiveJobName: {EffectiveJobName}",
AnonymousJobArgs.JobNameConstant,
DynamicBackgroundJobArgs.JobNameConstant,
args.JobName
);

2
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IDynamicBackgroundJobManager.cs

@ -15,7 +15,7 @@ public interface IDynamicBackgroundJobManager
/// If a typed job configuration exists for this name, the args will be
/// deserialized to the configured args type and enqueued through the typed pipeline.
/// If a dynamic handler is registered, the args will be wrapped as
/// <see cref="AnonymousJobArgs"/> and enqueued through the standard typed pipeline.
/// <see cref="DynamicBackgroundJobArgs"/> and enqueued through the standard typed pipeline.
/// </summary>
/// <param name="jobName">Name of the background job.</param>
/// <param name="args">Job arguments (will be serialized to JSON).</param>

4
framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/AbpDashboardOptionsProvider.cs

@ -24,9 +24,9 @@ public class AbpDashboardOptionsProvider : ITransientDependency
var jobName = job.ToString();
if (job.Args.Count == 3 && job.Args.Last() is CancellationToken)
{
if (job.Args[1] is AnonymousJobArgs anonymousJobArgs)
if (job.Args[1] is DynamicBackgroundJobArgs dynamicJobArgs)
{
jobName = anonymousJobArgs.JobName;
jobName = dynamicJobArgs.JobName;
}
else
{

8
framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobManager_Tests.cs

@ -73,7 +73,7 @@ public class BackgroundJobManager_Tests : BackgroundJobsTestBase
var jobInfo = await _backgroundJobStore.FindAsync(Guid.Parse(jobIdAsString));
jobInfo.ShouldNotBeNull();
jobInfo.JobName.ShouldBe(AnonymousJobArgs.JobNameConstant);
jobInfo.JobName.ShouldBe(DynamicBackgroundJobArgs.JobNameConstant);
jobInfo.JobArgs.ShouldContain("TestDynamicJob");
jobInfo.JobArgs.ShouldContain("ORD-001");
}
@ -86,8 +86,8 @@ public class BackgroundJobManager_Tests : BackgroundJobsTestBase
await _backgroundJobExecuter.ExecuteAsync(
new JobExecutionContext(
ServiceProvider,
typeof(AnonymousJobExecutorAsyncBackgroundJob),
new AnonymousJobArgs("TestDynamicJob", "{\"OrderId\":\"ORD-001\"}")
typeof(DynamicBackgroundJobExecutorJob),
new DynamicBackgroundJobArgs("TestDynamicJob", "{\"OrderId\":\"ORD-001\"}")
)
);
@ -106,7 +106,7 @@ public class BackgroundJobManager_Tests : BackgroundJobsTestBase
var jobInfo = await _backgroundJobStore.FindAsync(Guid.Parse(jobIdAsString));
jobInfo.ShouldNotBeNull();
jobInfo.JobName.ShouldBe(typedJobName);
jobInfo.JobName.ShouldNotBe(AnonymousJobArgs.JobNameConstant);
jobInfo.JobName.ShouldNotBe(DynamicBackgroundJobArgs.JobNameConstant);
}
[Fact]

4
modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/DemoAppSharedModule.cs

@ -15,7 +15,7 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared
{
var dynamicJobManager = context.ServiceProvider.GetRequiredService<IDynamicBackgroundJobManager>();
dynamicJobManager.RegisterHandler("CompileTimeAnonymousJob", (ctx, ct) =>
dynamicJobManager.RegisterHandler("CompileTimeDynamicJob", (ctx, ct) =>
{
using (var doc = JsonDocument.Parse(ctx.JsonData))
{
@ -24,7 +24,7 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared
: doc.RootElement.TryGetProperty("Value", out prop)
? prop.GetString()
: null;
Console.WriteLine($"[ANONYMOUS-COMPILE] {value}");
Console.WriteLine($"[DYNAMIC-COMPILE] {value}");
return Task.CompletedTask;
}
});

16
modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/SampleJobCreator.cs

@ -31,7 +31,7 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs
await _backgroundJobManager.EnqueueAsync(new WriteToConsoleYellowJobArgs { Value = "test 1 (yellow) - typed" });
// Register runtime dynamic handler
_dynamicBackgroundJobManager.RegisterHandler("RuntimeAnonymousJob", (context, ct) =>
_dynamicBackgroundJobManager.RegisterHandler("RuntimeDynamicJob", (context, ct) =>
{
using (var doc = JsonDocument.Parse(context.JsonData))
{
@ -40,7 +40,7 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs
: doc.RootElement.TryGetProperty("Value", out prop)
? prop.GetString()
: null;
Console.WriteLine($"[ANONYMOUS-RUNTIME] {value}");
Console.WriteLine($"[DYNAMIC-RUNTIME] {value}");
return Task.CompletedTask;
}
});
@ -58,21 +58,21 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs
// String-based enqueue with anonymous object (typed job path)
await _dynamicBackgroundJobManager.EnqueueAsync(
"GreenJob",
new { Value = "test 3 (green) - by name, anonymous", Time = DateTime.Now }
new { Value = "test 3 (green) - by name, dynamic", Time = DateTime.Now }
);
await _dynamicBackgroundJobManager.EnqueueAsync(
"YellowJob",
new { Value = "test 3 (yellow) - by name, anonymous", Time = DateTime.Now }
new { Value = "test 3 (yellow) - by name, dynamic", Time = DateTime.Now }
);
// Dynamic job handlers
await _dynamicBackgroundJobManager.EnqueueAsync(
"CompileTimeAnonymousJob",
new { Value = "test 4 (anonymous) - compile-time" }
"CompileTimeDynamicJob",
new { Value = "test 4 (dynamic) - compile-time" }
);
await _dynamicBackgroundJobManager.EnqueueAsync(
"RuntimeAnonymousJob",
new { Value = "test 5 (anonymous) - runtime" }
"RuntimeDynamicJob",
new { Value = "test 5 (dynamic) - runtime" }
);
}
}

Loading…
Cancel
Save