orchardcore-background-tasks
Skill for creating background tasks and scheduled jobs in Orchard Core. Covers IBackgroundTask implementation, scheduling configuration, and background service patterns.
Files2
View on GitHubSKILL.mdsrc/CrestApps.AgentSkills/orchardcore/orchardcore-background-tasks/SKILL.md
GitHubOrchard Core Background Tasks - Prompt Templates
Create Background Tasks
You are an Orchard Core expert. Generate background task implementations for Orchard Core.
Guidelines
- Background tasks implement
IBackgroundTaskand run on a cron schedule declared with[BackgroundTask]. - Tasks are registered in
Startup.csasIBackgroundTasksingletons. Inject only singleton-safe services in the constructor; resolve scoped services (ISession,IContentManager, ...) from theIServiceProviderpassed toDoWorkAsync. - Each run gets its own tenant shell scope. The schedule is evaluated in the site time zone.
[BackgroundTask]properties:Title,Schedule(default*/5 * * * *),Description,Enable(defaulttrue),LockTimeoutandLockExpiration(milliseconds), andUsePipeline(build the tenant routing pipeline so the task can generate URLs).- A task is atomic when both
LockTimeoutandLockExpirationare greater than0: on a distributed lock (for example Redis), only one instance runs it at a time. Lock values are not limited to 30 seconds. - Enable
OrchardCore.BackgroundTasksto manage tasks under Tools → Background Tasks: enable or disable, change the schedule, the lock values, andUsePipeline, overriding the attribute values. This requires theManageBackgroundTaskspermission. - The admin list supports bulk actions: select several tasks and use Actions → Enable or Disable.
- Use
ILoggerfor logging task execution and errors, and honor theCancellationToken. - Tasks should be idempotent and handle concurrent execution gracefully.
- Always seal classes.
Basic Background Task
using Microsoft.Extensions.Logging;
using OrchardCore.BackgroundTasks;
[BackgroundTask(
Title = "{{TaskTitle}}",
Schedule = "*/15 * * * *",
Description = "{{TaskDescription}}")]
public sealed class {{TaskName}} : IBackgroundTask
{
private readonly ILogger<{{TaskName}}> _logger;
public {{TaskName}}(ILogger<{{TaskName}}> logger)
{
_logger = logger;
}
public Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken)
{
_logger.LogInformation("Running {{TaskName}}...");
// Task logic here
return Task.CompletedTask;
}
}
Background Task with Service Dependencies
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OrchardCore.BackgroundTasks;
using OrchardCore.ContentManagement;
using OrchardCore.ContentManagement.Records;
using YesSql;
[BackgroundTask(
Title = "{{TaskTitle}}",
Schedule = "0 */6 * * *",
Description = "{{TaskDescription}}")]
public sealed class {{TaskName}} : IBackgroundTask
{
private readonly ILogger<{{TaskName}}> _logger;
public {{TaskName}}(ILogger<{{TaskName}}> logger)
{
_logger = logger;
}
public async Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken)
{
// Resolve scoped services from the service provider of the run.
var contentManager = serviceProvider.GetRequiredService<IContentManager>();
var session = serviceProvider.GetRequiredService<ISession>();
_logger.LogInformation("Running {{TaskName}}...");
var items = await session
.Query<ContentItem, ContentItemIndex>(x =>
x.ContentType == "{{ContentType}}" && x.Published)
.ListAsync();
var count = 0;
foreach (var item in items)
{
if (cancellationToken.IsCancellationRequested)
{
break;
}
// Process item
count++;
}
_logger.LogInformation("{{TaskName}} completed. Processed {Count} items.", count);
}
}
Atomic Background Task
Use a lock when the task must not run concurrently on several instances of a web farm. Values are in milliseconds and may exceed 30 seconds.
using OrchardCore.BackgroundTasks;
[BackgroundTask(
Title = "Nightly export",
Schedule = "0 3 * * *",
Description = "Exports data once per night across all instances.",
LockTimeout = 3_000,
LockExpiration = 300_000,
UsePipeline = true)]
public sealed class NightlyExportTask : IBackgroundTask
{
public Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken)
{
// Long running work; the lock is held up to 5 minutes.
return Task.CompletedTask;
}
}
Registering a Background Task
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.BackgroundTasks;
using OrchardCore.Modules;
public sealed class Startup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IBackgroundTask, {{TaskName}}>();
}
}
Reacting to Task Execution
Implement IBackgroundTaskEventHandler to run code before and after every task of the tenant (for example telemetry). BackgroundTaskEventContext exposes Name, Tenant, Services, and Exception.
using OrchardCore.BackgroundTasks;
public sealed class TaskTelemetryHandler : IBackgroundTaskEventHandler
{
public Task ExecutingAsync(BackgroundTaskEventContext context, CancellationToken cancellationToken)
=> Task.CompletedTask;
public Task ExecutedAsync(BackgroundTaskEventContext context, CancellationToken cancellationToken)
{
if (context.HasException)
{
// Report context.Exception for context.Name on context.Tenant.
}
return Task.CompletedTask;
}
}
services.AddScoped<IBackgroundTaskEventHandler, TaskTelemetryHandler>();
Background Service Configuration
The host polls every tenant's tasks. Configure it in appsettings.json of the startup project under OrchardCore:BackgroundService (the 3.x name OrchardCore_BackgroundService is deprecated):
{
"OrchardCore": {
"BackgroundService": {
"PollingTime": "00:01:00",
"MinimumIdleTime": "00:00:10",
"ShellWarmup": false
}
}
}
Enabling Background Tasks Feature
{
"steps": [
{
"name": "Feature",
"enable": [
"OrchardCore.BackgroundTasks"
],
"disable": []
}
]
}
Common Cron Schedule Expressions
* * * * *— Every minute.*/5 * * * *— Every 5 minutes.*/15 * * * *— Every 15 minutes.0 * * * *— Every hour.0 */6 * * *— Every 6 hours.0 0 * * *— Daily at midnight.0 0 * * 0— Weekly on Sunday at midnight.0 0 1 * *— Monthly on the 1st at midnight.
Upgrading from 3.x
- The Background Tasks admin list now renders through the shared
AdminListshape (list nameBackgroundTasks, seeorchardcore-admin-lists) and offers bulk Enable and Disable actions. - The editor no longer caps Lock timeout and Lock expiration at 30000 ms.
- Rename the
OrchardCore_BackgroundServiceconfiguration section toBackgroundService.
See references/background-tasks-examples.md for a complete example.