Scheduled Tasks
Create background tasks that run on a schedule in your nopCommerce plugins.
Overview
Scheduled tasks run in the background at configurable intervals. Use them for:
- Syncing data with external systems
- Sending batch emails
- Cleaning up old records
- Generating reports
- Any periodic maintenance
Creating a Scheduled Task
Step 1: Implement IScheduleTask
csharp
// Tasks/MySyncTask.cs
using Nop.Services.ScheduleTasks;
namespace Nop.Plugin.Misc.MyPlugin.Tasks;
public class MySyncTask : IScheduleTask
{
#region Fields
private readonly IMyService _myService;
private readonly ILogger<MySyncTask> _logger;
#endregion
#region Ctor
public MySyncTask(
IMyService myService,
ILogger<MySyncTask> logger)
{
_myService = myService;
_logger = logger;
}
#endregion
#region Methods
public async Task ExecuteAsync()
{
_logger.LogInformation("MySyncTask started at {Time}", DateTime.UtcNow);
try
{
var recordsProcessed = await _myService.SyncDataAsync();
_logger.LogInformation("MySyncTask completed. Processed {Count} records", recordsProcessed);
}
catch (Exception ex)
{
_logger.LogError(ex, "MySyncTask failed");
throw;
}
}
#endregion
}Step 2: Register the Task on Install
csharp
// MyPlugin.cs
public override async Task InstallAsync()
{
// Register the scheduled task
var scheduleTaskService = EngineContext.Current.Resolve<IScheduleTaskService>();
var task = new ScheduleTask
{
Name = "My Plugin - Data Sync",
Seconds = 3600, // Run every hour
Type = typeof(MySyncTask).FullName,
Enabled = true,
StopOnError = false
};
await scheduleTaskService.InsertTaskAsync(task);
await base.InstallAsync();
}
public override async Task UninstallAsync()
{
// Remove the scheduled task
var scheduleTaskService = EngineContext.Current.Resolve<IScheduleTaskService>();
var task = await scheduleTaskService.GetTaskByTypeAsync(typeof(MySyncTask).FullName);
if (task != null)
await scheduleTaskService.DeleteTaskAsync(task);
await base.UninstallAsync();
}Task Configuration
Schedule Properties
| Property | Description |
|---|---|
Name | Display name in admin |
Seconds | Interval between runs |
Type | Full type name of task class |
Enabled | Whether task is active |
StopOnError | Stop future runs on failure |
LastStartUtc | Last execution start time |
LastEndUtc | Last execution end time |
LastSuccessUtc | Last successful completion |
Common Intervals
csharp
// Every 5 minutes
Seconds = 300
// Every hour
Seconds = 3600
// Every 6 hours
Seconds = 21600
// Once daily
Seconds = 86400Advanced Patterns
Task with Progress Tracking
csharp
public class BatchProcessTask : IScheduleTask
{
#region Fields
private readonly IRepository<PendingItem> _repository;
private readonly ILogger<BatchProcessTask> _logger;
#endregion
#region Ctor
public BatchProcessTask(
IRepository<PendingItem> repository,
ILogger<BatchProcessTask> logger)
{
_repository = repository;
_logger = logger;
}
#endregion
#region Methods
public async Task ExecuteAsync()
{
var items = await _repository.GetAllAsync(q =>
q.Where(i => !i.Processed).Take(100));
var processed = 0;
var failed = 0;
foreach (var item in items)
{
try
{
await ProcessItemAsync(item);
item.Processed = true;
item.ProcessedOnUtc = DateTime.UtcNow;
await _repository.UpdateAsync(item);
processed++;
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to process item {ItemId}", item.Id);
failed++;
}
}
_logger.LogInformation(
"Batch completed: {Processed} processed, {Failed} failed",
processed, failed);
}
#endregion
#region Utilities
private async Task ProcessItemAsync(PendingItem item)
{
// Process logic here
}
#endregion
}Task with External API
csharp
public class ExternalSyncTask : IScheduleTask
{
#region Fields
private readonly IHttpClientFactory _httpClientFactory;
private readonly ISettingService _settingService;
private readonly IRepository<SyncRecord> _repository;
#endregion
#region Ctor
public ExternalSyncTask(
IHttpClientFactory httpClientFactory,
ISettingService settingService,
IRepository<SyncRecord> repository)
{
_httpClientFactory = httpClientFactory;
_settingService = settingService;
_repository = repository;
}
#endregion
#region Methods
public async Task ExecuteAsync()
{
var settings = await _settingService.LoadSettingAsync<MyPluginSettings>();
if (string.IsNullOrEmpty(settings.ApiKey))
throw new InvalidOperationException("API key not configured");
var client = _httpClientFactory.CreateClient();
client.DefaultRequestHeaders.Add("X-Api-Key", settings.ApiKey);
var response = await client.GetAsync(settings.ApiEndpoint);
response.EnsureSuccessStatusCode();
var data = await response.Content.ReadFromJsonAsync<List<ExternalItem>>();
foreach (var item in data)
{
await SyncItemAsync(item);
}
}
#endregion
#region Utilities
private async Task SyncItemAsync(ExternalItem item)
{
var existing = await _repository.GetAllAsync(q =>
q.Where(r => r.ExternalId == item.Id).FirstOrDefault());
if (existing == null)
{
await _repository.InsertAsync(new SyncRecord
{
ExternalId = item.Id,
Data = item.Data,
SyncedOnUtc = DateTime.UtcNow
});
}
else
{
existing.Data = item.Data;
existing.SyncedOnUtc = DateTime.UtcNow;
await _repository.UpdateAsync(existing);
}
}
#endregion
}Configurable Task Interval
csharp
// Allow admin to configure interval
public class MyPluginSettings : ISettings
{
public string ApiKey { get; set; }
public int SyncIntervalMinutes { get; set; } = 60;
}
// Update on settings change
public async Task<IActionResult> Configure(ConfigurationModel model)
{
var settings = await _settingService.LoadSettingAsync<MyPluginSettings>();
settings.SyncIntervalMinutes = model.SyncIntervalMinutes;
await _settingService.SaveSettingAsync(settings);
// Update task interval
var task = await _scheduleTaskService.GetTaskByTypeAsync(typeof(MySyncTask).FullName);
if (task != null)
{
task.Seconds = model.SyncIntervalMinutes * 60;
await _scheduleTaskService.UpdateTaskAsync(task);
}
return await Configure();
}Manual Execution
Run a task manually from admin or via API:
csharp
// In a controller action
public async Task<IActionResult> RunSyncNow()
{
var task = EngineContext.Current.Resolve<MySyncTask>();
await task.ExecuteAsync();
_notificationService.SuccessNotification("Sync completed successfully");
return RedirectToAction("Configure");
}Best Practices
- Keep tasks focused - One task, one responsibility
- Process in batches - Don't load all records at once
- Log progress - Track what's being processed
- Handle failures gracefully - Don't crash the entire task
- Use transactions carefully - Commit after each logical unit
- Respect timeouts - Tasks shouldn't run indefinitely
- Test thoroughly - Simulate long-running scenarios
Monitoring Tasks
View task status in Admin > System > Schedule Tasks:
- Last run time and duration
- Success/failure status
- Enable/disable toggle
- Manual run button
Next Steps
- Configuration - Plugin settings
- Events - Event-driven patterns
- Best Practices - Optimization