TCM.Tasks
1.0.21
dotnet add package TCM.Tasks --version 1.0.21
NuGet\Install-Package TCM.Tasks -Version 1.0.21
<PackageReference Include="TCM.Tasks" Version="1.0.21" />
<PackageVersion Include="TCM.Tasks" Version="1.0.21" />
<PackageReference Include="TCM.Tasks" />
paket add TCM.Tasks --version 1.0.21
#r "nuget: TCM.Tasks, 1.0.21"
#:package TCM.Tasks@1.0.21
#addin nuget:?package=TCM.Tasks&version=1.0.21
#tool nuget:?package=TCM.Tasks&version=1.0.21
TCM.Tasks
Distributed task scheduling library for TCM services, built on Quartz.NET. Provides persistent, clustered background job scheduling with auto-discovery of job implementations and a MediatR-based management API.
Features
- Quartz.NET Integration — Persistent job store backed by SQL Server
- Kubernetes Clustering — Built-in cluster support for distributed deployments
- Auto Job Discovery — Automatically discovers and registers all
IQuartzJobimplementations at startup - Cron & Interval Scheduling — Supports standard cron expressions and time-interval triggers
- Run-Now Support — Trigger any job immediately via MediatR query or REST API
- Execution Tracking — Records last start, last success, and last failure timestamps per job
- CQRS API — MediatR commands and queries to create, update, and query scheduled tasks
- REST API — Optional controllers for CRUD (
AddTCMTasksControllers) - Lease-Based Clustering — Prevents duplicate execution across nodes via machine-name leasing
Installation
dotnet add package TCM.Tasks
Note:
TCM.Tasksdepends onTCM.Core. Both packages must be installed.
Requirements
- .NET 9.0
- SQL Server (for Quartz persistent store and
ScheduleTasktable) - TCM.Core
Getting Started
Register Services
Tasks uses a dedicated SQL Server database shared by EF (ScheduleTask) and Quartz (qrtz_* tables).
builder.Services.AddTCMTasks(builder.Configuration);
// Or pass connection string and/or app id explicitly
builder.Services.AddTCMTasks(builder.Configuration, myConnectionString, appId: "MyApp");
| Registration | DbContext | Migrations |
|---|---|---|
AddTCMTasks |
TasksDbContext |
ScheduleTask |
Each host application should set TASK_APP_ID so tasks and Quartz jobs are isolated when multiple apps share the same database. Legacy rows may keep AppId null until you backfill them manually.
Quartz scheduler instance: tcm-tasks-{appId} (or tcm-tasks-default when unset). Job group: {appId}.BackgroundTask (or BackgroundTask when unset).
HTTP API (controllers)
builder.Services.AddControllers()
.AddTCMTasksControllers();
Ensure the host configures API versioning (v1.0), same as other TCM APIs.
| Method | Route | Description |
|---|---|---|
| GET | api/v1/ScheduleTask |
Paged list |
| GET | api/v1/ScheduleTask/{id} |
Get by Guid |
| POST | api/v1/ScheduleTask |
Create |
| PUT | api/v1/ScheduleTask/{id} |
Update |
| DELETE | api/v1/ScheduleTask/{id} |
Delete (+ remove Quartz job) |
| POST | api/v1/ScheduleTask/{id}/run-now |
Execute immediately |
InAppAuth (optional, recommended)
TCM.Tasks references TCM.InAppAuth. Controllers use [AppPermission]; handlers apply data scope on ScheduleTask via IScopedEntity.OrgUnitId.
builder.Services.AddInAppAuthentication(builder.Configuration);
builder.Services.AddTCMTasks(builder.Configuration);
builder.Services.AddControllers()
.AddInAppAuthControllers()
.AddTCMTasksControllers();
Align app ids: IN_APP_AUTH_APP_ID and TASK_APP_ID should resolve to the same value (e.g. Tasks → resource slug tasks:schedule-task).
| Step | Action |
|---|---|
| 1 | Run migrations: InAppAuth + Tasks (AddScheduleTaskOrgUnitId) |
| 2 | POST /api/v1/Resource/sync — register schedule-task + run-now |
| 3 | Grant role permissions in admin UI |
| 4 | Optional: POST /api/v1/DataScope/MigrateAllowOwnOnly |
| 5 | Configure RoleResourceDataScope; set enforcement Off → LogOnly → Enforce |
Records without OrgUnitId are only visible when scope is All. On create, OrgUnitId defaults to the user's primary org; returns 400 if missing (unless scope is All).
Connection string
Same database for EF and Quartz. Resolution order:
connectionStringargument toAddTCMTasks(if provided)- Otherwise
TASK_CONNTECTION_STRING:- Development:
appsettings.jsonkeyTASK_CONNTECTION_STRING - Other environments: environment variable
TASK_CONNTECTION_STRING, then appsettings fallback
- Development:
appsettings.json (Development):
{
"TASK_CONNTECTION_STRING": "Server=...;Database=TCM.Tasks;User Id=...;Password=...;TrustServerCertificate=True",
"TASK_APP_ID": "MyApp"
}
Production / container:
TASK_CONNTECTION_STRING="Server=...;Database=TCM.Tasks;..."
TASK_APP_ID="MyApp"
EF migrations
Use TasksDbContext for all migrations.
Package Manager Console (default project = TCM.Tasks or migrations project):
Add-Migration AddScheduleTasks -Context TasksDbContext -Project TCM.Tasks -StartupProject TCM.Tasks
Update-Database -Context TasksDbContext -Project TCM.Tasks -StartupProject TCM.Tasks
dotnet ef:
dotnet ef migrations add AddScheduleTasks --project TCM.Tasks --startup-project TCM.Tasks --context TasksDbContext
dotnet ef database update --project TCM.Tasks --startup-project TCM.Tasks --context TasksDbContext
Design-time: TASK_CONNTECTION_STRING in appsettings.json of the startup project (or env var).
Initialize Quartz Tables
On first run, call DatabaseExtension.InitializeAsync() to create the required Quartz SQL Server tables. The SQL script is embedded in the library.
await app.Services.InitializeTaskDatabaseAsync();
Define a Job
public class SendReportJob : IQuartzJob
{
public async Task Execute(IJobExecutionContext context)
{
// job logic here
}
public async Task Execute(string id, bool checkRun)
{
await Execute(id);
}
public async Task Execute(string id)
{
// run with specific task ID
}
}
All classes implementing IQuartzJob are auto-discovered at startup and synced with the database.
Manage Jobs via MediatR
await mediator.Send(new CreateScheduleTaskCommand { ... });
await mediator.Send(new UpdateScheduleTaskCommand { Id = taskId, Enabled = false });
var tasks = await mediator.Send(new ScheduleTasksQuery());
await mediator.Send(new ScheduleTaskRunNowQuery { Id = taskId });
Project Structure
TCM.Tasks/
├── Domain/ ScheduleTask, IScheduleTaskDefinition
├── Models/ ScheduleTaskModel
├── Infrastructure/ ScheduleTaskRepository
├── TypeConfigurations/
├── Contexts/ TasksDbContext
├── Command/ Create, Update, Delete
├── Query/ List, Get, RunNow
├── Controllers/v1/ ScheduleTaskController
├── Quartz/ QuartzJobExecute, IQuartzJob
└── Extensions/ AddTCMTasks, UseQuartz
ScheduleTask Entity
| Field | Description |
|---|---|
AppId |
Host application identifier (nullable for legacy rows; set via TASK_APP_ID) |
ScheduleTaskName |
Display name of the job |
Type |
Fully qualified type name of the IQuartzJob implementation |
CronExpression |
Standard Quartz cron expression (takes priority over interval) |
TimeInterval |
Fallback interval in seconds when no cron expression is set |
Enabled |
Whether the job is active |
StopOnError |
Disable the job automatically on execution failure |
LastStartUtc |
Last time the job started |
LastSuccessUtc |
Last time the job completed successfully |
LastNonSuccessEndUtc |
Last time the job ended with an error |
LeasedByMachineName |
Node currently holding the execution lease (clustering) |
LeasedUntilUtc |
Lease expiry time (clustering) |
Key Dependencies
| Package | Purpose |
|---|---|
| Quartz.AspNetCore | Quartz.NET ASP.NET Core integration |
| Quartz.Extensions.DependencyInjection | DI support for Quartz |
| Quartz.Serialization.Json | JSON job data serialization |
| TCM.Core | Repositories, BaseEntity, TypeFinder, MediatR, etc. |
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net7.0
- Microsoft.Data.SqlClient (>= 5.2.2)
- Microsoft.Extensions.Http (>= 7.0.20)
- Quartz.AspNetCore (>= 3.16.1)
- Quartz.Extensions.DependencyInjection (>= 3.16.1)
- Quartz.Serialization.Json (>= 3.16.1)
- TCM.Core (>= 1.0.18)
- TCM.InAppAuth (>= 1.0.21)
-
net9.0
- Microsoft.Data.SqlClient (>= 6.1.2)
- Microsoft.Extensions.Http (>= 10.0.5)
- Quartz.AspNetCore (>= 3.16.1)
- Quartz.Extensions.DependencyInjection (>= 3.16.1)
- Quartz.Serialization.Json (>= 3.16.1)
- TCM.Core (>= 1.0.18)
- TCM.InAppAuth (>= 1.0.21)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.21 | 91 | 6/2/2026 |
| 1.0.20 | 82 | 6/2/2026 |
| 1.0.19 | 105 | 5/26/2026 |
| 1.0.18 | 88 | 5/25/2026 |
| 1.0.17 | 88 | 5/25/2026 |
| 1.0.16 | 87 | 5/25/2026 |
| 1.0.15 | 92 | 5/24/2026 |
| 1.0.14 | 106 | 5/24/2026 |
| 1.0.13 | 87 | 5/24/2026 |
| 1.0.11 | 93 | 5/24/2026 |
| 1.0.10 | 91 | 5/24/2026 |
| 1.0.9 | 105 | 5/23/2026 |
| 1.0.8 | 106 | 5/23/2026 |
| 1.0.5 | 95 | 5/23/2026 |
| 1.0.4 | 89 | 5/23/2026 |
| 1.0.3 | 115 | 4/6/2026 |
| 1.0.2 | 107 | 3/23/2026 |
| 1.0.1 | 105 | 3/18/2026 |
| 1.0.0 | 101 | 3/18/2026 |