mirror of https://github.com/abpframework/abp.git
Browse Source
[Community] Implementing Background Jobs with ABP and Hangfire Community article from ABP.io AI Post Generator. **Folder:** `2026-06-22-implementing-background-jobs-with-abp-and-hangfire` **Author:** adminpull/25669/head
6 changed files with 607 additions and 0 deletions
@ -0,0 +1,607 @@ |
|||||
|
Background jobs are one of those features that look simple at first and become operationally important very quickly. Sending emails, generating reports, syncing with third-party APIs, cleaning expired data, and processing imports should not block your HTTP requests. |
||||
|
|
||||
|
ABP gives you a clean abstraction for background jobs, and Hangfire gives you a production-friendly execution engine with persistence, retries, queues, and a dashboard. The useful part is that you can keep your application code aligned with ABP’s abstractions while swapping in Hangfire as the actual runner. |
||||
|
|
||||
|
In this article, I’ll walk through how to implement background jobs with ABP and Hangfire, when to use each piece, and where teams usually get tripped up. |
||||
|
|
||||
|
## Why use Hangfire instead of ABP's default background job manager? |
||||
|
|
||||
|
ABP already has a built-in background job system, and it is perfectly fine for simple cases. But it helps to understand what you are trading. |
||||
|
|
||||
|
### ABP default job manager |
||||
|
|
||||
|
By default, ABP background jobs are: |
||||
|
|
||||
|
- Enqueued through `IBackgroundJobManager` |
||||
|
- Executed in-process |
||||
|
- FIFO-oriented |
||||
|
- Single-threaded by default |
||||
|
- Retried automatically with increasing delays |
||||
|
- Stored through ABP's background job store |
||||
|
|
||||
|
This is good when: |
||||
|
|
||||
|
- Your app is small or moderate in workload |
||||
|
- You want minimal setup |
||||
|
- You do not need a dashboard |
||||
|
- You do not need advanced queue management |
||||
|
|
||||
|
### Hangfire integration |
||||
|
|
||||
|
When you add `Volo.Abp.BackgroundJobs.HangFire`, ABP can keep the same `IBackgroundJobManager` programming model, but Hangfire becomes the execution backend. |
||||
|
|
||||
|
That gives you: |
||||
|
|
||||
|
- Durable job storage |
||||
|
- Better operational visibility through the Hangfire dashboard |
||||
|
- Multiple worker servers |
||||
|
- Queue-based processing |
||||
|
- Recurring jobs and scheduling features |
||||
|
- A mature retry and monitoring model |
||||
|
|
||||
|
In practice, Hangfire is the better choice when background processing is part of the actual system design, not just a convenience. |
||||
|
|
||||
|
### Quick comparison |
||||
|
|
||||
|
Use ABP default when: |
||||
|
|
||||
|
- You want the simplest possible setup |
||||
|
- Background jobs are low volume |
||||
|
- A single app instance is enough |
||||
|
- You do not need a dashboard or queue controls |
||||
|
|
||||
|
Use Hangfire when: |
||||
|
|
||||
|
- You need reliability across restarts |
||||
|
- You run multiple instances |
||||
|
- You need recurring jobs or queue isolation |
||||
|
- You want to inspect failures and retries visually |
||||
|
- Background processing is operationally important |
||||
|
|
||||
|
|
||||
|
|
||||
|
 |
||||
|
|
||||
|
## Defining a background job in ABP |
||||
|
|
||||
|
The nice part of ABP is that your job code does not need to know about Hangfire. |
||||
|
|
||||
|
Start with a job arguments class: |
||||
|
|
||||
|
```csharp |
||||
|
public class EmailSendingArgs |
||||
|
{ |
||||
|
public string To { get; set; } = string.Empty; |
||||
|
public string Subject { get; set; } = string.Empty; |
||||
|
public string Body { get; set; } = string.Empty; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
Then create the job itself: |
||||
|
|
||||
|
```csharp |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.BackgroundJobs; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
public class EmailSendingJob : AsyncBackgroundJob<EmailSendingArgs>, ITransientDependency |
||||
|
{ |
||||
|
private readonly IEmailSender _emailSender; |
||||
|
|
||||
|
public EmailSendingJob(IEmailSender emailSender) |
||||
|
{ |
||||
|
_emailSender = emailSender; |
||||
|
} |
||||
|
|
||||
|
public override async Task ExecuteAsync(EmailSendingArgs args) |
||||
|
{ |
||||
|
await _emailSender.SendAsync( |
||||
|
args.To, |
||||
|
args.Subject, |
||||
|
args.Body |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
This job works with ABP’s job abstraction regardless of whether the runtime backend is the default implementation or Hangfire. |
||||
|
|
||||
|
To enqueue it: |
||||
|
|
||||
|
```csharp |
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.BackgroundJobs; |
||||
|
|
||||
|
public class NotificationAppService : ApplicationService |
||||
|
{ |
||||
|
private readonly IBackgroundJobManager _backgroundJobManager; |
||||
|
|
||||
|
public NotificationAppService(IBackgroundJobManager backgroundJobManager) |
||||
|
{ |
||||
|
_backgroundJobManager = backgroundJobManager; |
||||
|
} |
||||
|
|
||||
|
public async Task QueueWelcomeEmailAsync(string email) |
||||
|
{ |
||||
|
await _backgroundJobManager.EnqueueAsync( |
||||
|
new EmailSendingArgs |
||||
|
{ |
||||
|
To = email, |
||||
|
Subject = "Welcome", |
||||
|
Body = "Your account is ready." |
||||
|
}, |
||||
|
priority: BackgroundJobPriority.Normal, |
||||
|
delay: TimeSpan.FromMinutes(1) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
A few practical notes: |
||||
|
|
||||
|
- `delay` is useful for short deferrals and back-office workflows. |
||||
|
- `priority` is part of ABP’s abstraction. How it maps operationally depends on the provider. |
||||
|
- Keep argument objects small and serializable. |
||||
|
- Do not pass EF entities or large object graphs into jobs. |
||||
|
|
||||
|
## Setting up Hangfire in an ABP application |
||||
|
|
||||
|
To integrate Hangfire, install the package and wire it into your ABP module. |
||||
|
|
||||
|
### 1. Add the package |
||||
|
|
||||
|
Using ABP CLI: |
||||
|
|
||||
|
```bash |
||||
|
abp add-package Volo.Abp.BackgroundJobs.HangFire |
||||
|
``` |
||||
|
|
||||
|
Or with NuGet: |
||||
|
|
||||
|
```bash |
||||
|
Install-Package Volo.Abp.BackgroundJobs.HangFire |
||||
|
``` |
||||
|
|
||||
|
### 2. Add the module dependency |
||||
|
|
||||
|
Typically this goes into your host module, such as `HttpApiHostModule`: |
||||
|
|
||||
|
```csharp |
||||
|
using Volo.Abp.BackgroundJobs.Hangfire; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpBackgroundJobsHangfireModule) |
||||
|
)] |
||||
|
public class MyProjectHttpApiHostModule : AbpModule |
||||
|
{ |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 3. Configure Hangfire services |
||||
|
|
||||
|
In `ConfigureServices`: |
||||
|
|
||||
|
```csharp |
||||
|
using Hangfire; |
||||
|
using Microsoft.Extensions.Configuration; |
||||
|
|
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
var configuration = context.Services.GetConfiguration(); |
||||
|
|
||||
|
context.Services.AddHangfire(config => |
||||
|
{ |
||||
|
config.UseSqlServerStorage( |
||||
|
configuration.GetConnectionString("Default") |
||||
|
); |
||||
|
}); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
If you use PostgreSQL, Redis, or another Hangfire storage provider, configure that instead. The storage decision matters because all servers that process jobs must share the same backing store. |
||||
|
|
||||
|
### 4. Enable the Hangfire dashboard |
||||
|
|
||||
|
In `OnApplicationInitialization`: |
||||
|
|
||||
|
```csharp |
||||
|
using Microsoft.AspNetCore.Builder; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
|
||||
|
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
||||
|
{ |
||||
|
var app = context.GetApplicationBuilder(); |
||||
|
|
||||
|
app.UseAuthentication(); |
||||
|
app.UseAuthorization(); |
||||
|
|
||||
|
app.UseAbpHangfireDashboard(); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
The dashboard middleware should be added after authentication and authorization middleware. |
||||
|
|
||||
|
At this point, jobs enqueued through `IBackgroundJobManager` should use Hangfire as long as the integration is correctly activated. |
||||
|
|
||||
|
|
||||
|
|
||||
|
 |
||||
|
|
||||
|
## End-to-end example: offloading a report export |
||||
|
|
||||
|
A common use case is exporting a report that may take several seconds or minutes. |
||||
|
|
||||
|
Instead of generating the file during the HTTP request: |
||||
|
|
||||
|
- Save an export request record |
||||
|
- Enqueue a background job |
||||
|
- Let the job generate the file |
||||
|
- Notify the user when it is ready |
||||
|
|
||||
|
### Arguments |
||||
|
|
||||
|
```csharp |
||||
|
public class ReportExportJobArgs |
||||
|
{ |
||||
|
public Guid ExportRequestId { get; set; } |
||||
|
public Guid UserId { get; set; } |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Job implementation |
||||
|
|
||||
|
```csharp |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.BackgroundJobs; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Uow; |
||||
|
|
||||
|
public class ReportExportJob : AsyncBackgroundJob<ReportExportJobArgs>, ITransientDependency |
||||
|
{ |
||||
|
private readonly IReportExportAppService _reportExportAppService; |
||||
|
|
||||
|
public ReportExportJob(IReportExportAppService reportExportAppService) |
||||
|
{ |
||||
|
_reportExportAppService = reportExportAppService; |
||||
|
} |
||||
|
|
||||
|
public override async Task ExecuteAsync(ReportExportJobArgs args) |
||||
|
{ |
||||
|
await _reportExportAppService.GenerateAsync(args.ExportRequestId, args.UserId); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Enqueue from app service |
||||
|
|
||||
|
```csharp |
||||
|
public async Task<Guid> RequestExportAsync() |
||||
|
{ |
||||
|
var exportRequestId = GuidGenerator.Create(); |
||||
|
|
||||
|
await _backgroundJobManager.EnqueueAsync( |
||||
|
new ReportExportJobArgs |
||||
|
{ |
||||
|
ExportRequestId = exportRequestId, |
||||
|
UserId = CurrentUser.GetId() |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
return exportRequestId; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
This pattern scales much better than holding open a web request while doing CPU-heavy or IO-heavy work. |
||||
|
|
||||
|
## Retries, exceptions, and cancellation |
||||
|
|
||||
|
ABP and Hangfire both care about retries, but you should still design jobs carefully. |
||||
|
|
||||
|
### How ABP behaves |
||||
|
|
||||
|
With ABP background jobs: |
||||
|
|
||||
|
- Unhandled exceptions trigger retries |
||||
|
- Retry intervals increase over time |
||||
|
- Default implementation uses exponential backoff behavior |
||||
|
- Jobs may eventually time out or be marked abandoned depending on configuration |
||||
|
|
||||
|
### What this means for your code |
||||
|
|
||||
|
A job should be: |
||||
|
|
||||
|
- Idempotent whenever possible |
||||
|
- Safe to retry |
||||
|
- Explicit about transient vs permanent failures |
||||
|
|
||||
|
For example, sending the same payment capture twice is dangerous. Sending the same “your report is ready” notification twice is annoying but manageable. Design around the difference. |
||||
|
|
||||
|
### Cancellation handling |
||||
|
|
||||
|
If you use `ICancellationTokenProvider`, be deliberate. If cancellation means “try again later,” let the exception flow. If cancellation means “stop and do not retry,” return gracefully. |
||||
|
|
||||
|
Example: |
||||
|
|
||||
|
```csharp |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.BackgroundJobs; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
public class DataSyncJob : AsyncBackgroundJob<int> |
||||
|
{ |
||||
|
private readonly ICancellationTokenProvider _cancellationTokenProvider; |
||||
|
|
||||
|
public DataSyncJob(ICancellationTokenProvider cancellationTokenProvider) |
||||
|
{ |
||||
|
_cancellationTokenProvider = cancellationTokenProvider; |
||||
|
} |
||||
|
|
||||
|
public override async Task ExecuteAsync(int args) |
||||
|
{ |
||||
|
var cancellationToken = _cancellationTokenProvider.Token; |
||||
|
|
||||
|
cancellationToken.ThrowIfCancellationRequested(); |
||||
|
|
||||
|
await Task.Delay(500, cancellationToken); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Practical guidance |
||||
|
|
||||
|
- Keep jobs short and composable |
||||
|
- Persist progress if the job is large |
||||
|
- Use domain/application services inside jobs instead of putting business logic directly into the job class |
||||
|
- Log enough context to diagnose retries and failures |
||||
|
|
||||
|
## Recurring jobs and periodic work |
||||
|
|
||||
|
Not every background task is a one-time job. |
||||
|
|
||||
|
There are two different patterns: |
||||
|
|
||||
|
- Background jobs: one-off, delayed, or fire-and-forget work |
||||
|
- Background workers: periodic or recurring work |
||||
|
|
||||
|
In ABP, recurring processing is usually modeled with background workers rather than standard background jobs. |
||||
|
|
||||
|
### When to use a worker instead of a job |
||||
|
|
||||
|
Use a worker when you need: |
||||
|
|
||||
|
- A scheduled cleanup task |
||||
|
- A recurring sync with another system |
||||
|
- Polling behavior |
||||
|
- A cron-like schedule |
||||
|
|
||||
|
### Hangfire-backed recurring worker |
||||
|
|
||||
|
With Hangfire integration, you can derive from `HangfireBackgroundWorkerBase` and provide a cron expression. |
||||
|
|
||||
|
```csharp |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.BackgroundWorkers.Hangfire; |
||||
|
|
||||
|
public class ExpiredSessionsCleanupWorker : HangfireBackgroundWorkerBase |
||||
|
{ |
||||
|
private readonly ISessionCleanupService _sessionCleanupService; |
||||
|
|
||||
|
public ExpiredSessionsCleanupWorker(ISessionCleanupService sessionCleanupService) |
||||
|
{ |
||||
|
_sessionCleanupService = sessionCleanupService; |
||||
|
|
||||
|
RecurringJobId = "expired-sessions-cleanup"; |
||||
|
CronExpression = "0 * * * *"; |
||||
|
} |
||||
|
|
||||
|
public override async Task DoWorkAsync() |
||||
|
{ |
||||
|
await _sessionCleanupService.CleanupAsync(); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
A few details matter here: |
||||
|
|
||||
|
- `RecurringJobId` should be stable and unique. |
||||
|
- `CronExpression` controls the schedule. |
||||
|
- Hangfire recurring scheduling is minute-based in normal use, so do not expect second-level precision. |
||||
|
|
||||
|
### Background jobs vs background workers |
||||
|
|
||||
|
A simple rule: |
||||
|
|
||||
|
- If a user action creates work to do later, use a background job. |
||||
|
- If the system itself needs to run something on a schedule, use a background worker. |
||||
|
|
||||
|
|
||||
|
|
||||
|
 |
||||
|
|
||||
|
## Queue isolation and scaling across multiple instances |
||||
|
|
||||
|
Once you run more than one application instance, background processing becomes an architecture concern rather than a coding detail. |
||||
|
|
||||
|
### Shared storage is required |
||||
|
|
||||
|
If multiple nodes are going to process Hangfire jobs, they must share the same Hangfire storage. |
||||
|
|
||||
|
Typical setups include: |
||||
|
|
||||
|
- Multiple web instances + one shared SQL Server storage |
||||
|
- Web instances enqueueing jobs + dedicated worker instances processing them |
||||
|
- Separate deployment slots or services sharing the same Hangfire backend |
||||
|
|
||||
|
### Disabling execution on some nodes |
||||
|
|
||||
|
Sometimes you want your web app to enqueue jobs but not execute them. |
||||
|
|
||||
|
ABP supports this: |
||||
|
|
||||
|
```csharp |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.BackgroundJobs; |
||||
|
|
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpBackgroundJobOptions>(options => |
||||
|
{ |
||||
|
options.IsJobExecutionEnabled = false; |
||||
|
}); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
This is useful when: |
||||
|
|
||||
|
- You run dedicated worker processes |
||||
|
- You want predictable resource allocation |
||||
|
- You do not want front-end nodes competing for background work |
||||
|
|
||||
|
### Queue prefixing in clustered environments |
||||
|
|
||||
|
If multiple applications share the same Hangfire storage, isolate queues intentionally. |
||||
|
|
||||
|
For Hangfire integration in ABP, use `AbpHangfireOptions.DefaultQueuePrefix` to avoid queue collisions between different applications or environments. |
||||
|
|
||||
|
That matters more than teams expect. Without isolation, staging and production can end up looking at the same queues if storage is misconfigured. |
||||
|
|
||||
|
### Queue routing |
||||
|
|
||||
|
Hangfire supports multiple queues, and ABP’s Hangfire integration can route jobs based on conventions or attributes. |
||||
|
|
||||
|
In some scenarios, you may want specific jobs to go to specific queues, for example: |
||||
|
|
||||
|
- `emails` |
||||
|
- `exports` |
||||
|
- `integration` |
||||
|
- `critical` |
||||
|
|
||||
|
This is especially helpful when one queue can become noisy and starve more important work. |
||||
|
|
||||
|
|
||||
|
|
||||
|
 |
||||
|
|
||||
|
## Securing the Hangfire dashboard |
||||
|
|
||||
|
The Hangfire dashboard is extremely useful, but it is also an operations surface. Do not expose it casually. |
||||
|
|
||||
|
ABP provides authorization support for the dashboard via `AbpHangfireAuthorizationFilter`. |
||||
|
|
||||
|
A typical setup is to: |
||||
|
|
||||
|
- Require authentication |
||||
|
- Restrict by permission or role |
||||
|
- Optionally consider tenant-specific access rules |
||||
|
|
||||
|
Example: |
||||
|
|
||||
|
```csharp |
||||
|
app.UseAbpHangfireDashboard("/hangfire", new DashboardOptions |
||||
|
{ |
||||
|
Authorization = new[] |
||||
|
{ |
||||
|
new AbpHangfireAuthorizationFilter(requiredPermissionName: "Administration.Hangfire") |
||||
|
} |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
Even if your app is internal, treat the dashboard like an admin area: |
||||
|
|
||||
|
- Put it behind authorization |
||||
|
- Avoid exposing it publicly without network restrictions |
||||
|
- Audit who can retry or inspect jobs |
||||
|
|
||||
|
## Common pitfalls and behavior differences |
||||
|
|
||||
|
This is the part that usually saves the most time. |
||||
|
|
||||
|
### 1. Jobs still land in `AbpBackgroundJob` instead of Hangfire |
||||
|
|
||||
|
If Hangfire is not properly activated, ABP may continue using its native background job storage and you will see jobs in the `AbpBackgroundJob` table instead of Hangfire storage. |
||||
|
|
||||
|
Check these first: |
||||
|
|
||||
|
- The `Volo.Abp.BackgroundJobs.HangFire` package is installed |
||||
|
- `AbpBackgroundJobsHangfireModule` is added in `[DependsOn]` |
||||
|
- `AddHangfire(...)` is configured correctly |
||||
|
- The application starts with the expected module graph |
||||
|
|
||||
|
If any of those are missing, you may think you are using Hangfire while you are actually still on the default provider. |
||||
|
|
||||
|
### 2. Passing large or complex objects into jobs |
||||
|
|
||||
|
Keep job args small. Prefer identifiers over rich objects. |
||||
|
|
||||
|
Good: |
||||
|
|
||||
|
- `OrderId` |
||||
|
- `UserId` |
||||
|
- `ExportRequestId` |
||||
|
|
||||
|
Bad: |
||||
|
|
||||
|
- Full EF entities |
||||
|
- Large DTO graphs |
||||
|
- Objects with lazy-loading behavior or runtime-only state |
||||
|
|
||||
|
### 3. Non-idempotent job logic |
||||
|
|
||||
|
Retries will happen. If running the same job twice can corrupt data, redesign the workflow. |
||||
|
|
||||
|
Common fixes: |
||||
|
|
||||
|
- Add a processed flag |
||||
|
- Use unique constraints where appropriate |
||||
|
- Check prior execution status before applying side effects |
||||
|
- Make external calls with idempotency keys when supported |
||||
|
|
||||
|
### 4. Assuming recurring jobs run with exact timing |
||||
|
|
||||
|
Hangfire recurring jobs are cron-based and typically evaluated on minute boundaries. That is fine for most scheduled business work, but it is not a real-time scheduler. |
||||
|
|
||||
|
### 5. Ignoring queue isolation in multi-app environments |
||||
|
|
||||
|
If several apps share one Hangfire store, queue naming and prefixing must be explicit. Otherwise, one application can accidentally process another application's jobs. |
||||
|
|
||||
|
## When to use / When NOT to use ABP + Hangfire |
||||
|
|
||||
|
### Use ABP + Hangfire when |
||||
|
|
||||
|
- You want ABP-friendly job abstractions with a stronger execution backend |
||||
|
- You need operational visibility and retry inspection |
||||
|
- You run multiple instances or worker nodes |
||||
|
- You have recurring background tasks |
||||
|
- Your jobs are part of business-critical workflows |
||||
|
|
||||
|
### Do NOT use it when |
||||
|
|
||||
|
- The work must complete synchronously before responding to the user |
||||
|
- The task is so trivial that plain in-memory processing is enough |
||||
|
- You need event streaming rather than job scheduling |
||||
|
- You need ultra-low-latency real-time processing with very tight timing guarantees |
||||
|
|
||||
|
For many line-of-business systems, ABP + Hangfire hits a very practical middle ground: easy enough to implement, strong enough to operate. |
||||
|
|
||||
|
## A production-minded implementation checklist |
||||
|
|
||||
|
Before shipping, verify these points: |
||||
|
|
||||
|
- Jobs are enqueued through `IBackgroundJobManager` unless you explicitly need Hangfire-specific APIs |
||||
|
- Job arguments are small and serializable |
||||
|
- Job logic is retry-safe and preferably idempotent |
||||
|
- Hangfire storage is shared by all processing nodes |
||||
|
- Dashboard access is restricted |
||||
|
- Queue names or prefixes are isolated per app/environment |
||||
|
- Long-running jobs are split into manageable steps where possible |
||||
|
- You know which nodes execute jobs and which only enqueue them |
||||
|
|
||||
|
## TL;DR |
||||
|
|
||||
|
- ABP gives you a clean background job abstraction; Hangfire gives you the production-grade execution engine. |
||||
|
- Keep using `IBackgroundJobManager` for most jobs so your application code stays provider-independent. |
||||
|
- Use background jobs for one-off work and Hangfire-backed background workers for recurring tasks. |
||||
|
- In multi-instance deployments, shared storage, queue isolation, and dashboard security are not optional. |
||||
|
- If jobs still go to `AbpBackgroundJob`, your Hangfire integration is probably not fully activated. |
||||
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 1007 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
Loading…
Reference in new issue