Tickwork 1.1.1
dotnet add package Tickwork --version 1.1.1
NuGet\Install-Package Tickwork -Version 1.1.1
<PackageReference Include="Tickwork" Version="1.1.1" />
<PackageVersion Include="Tickwork" Version="1.1.1" />
<PackageReference Include="Tickwork" />
paket add Tickwork --version 1.1.1
#r "nuget: Tickwork, 1.1.1"
#:package Tickwork@1.1.1
#addin nuget:?package=Tickwork&version=1.1.1
#tool nuget:?package=Tickwork&version=1.1.1
Tickwork
Tickwork is a background job scheduler for .NET. It runs one-off jobs at a given time and recurring jobs on a cron expression, with retries, priorities, per-function concurrency limits and chained jobs (a child that runs when its parent succeeds, fails or is cancelled). Jobs are registered at compile time by a source generator, and are kept in memory or in your own database through Entity Framework Core.
Tickwork targets .NET 10 and .NET 11 and is distributed under the MIT License.
It is a fork of TickerQ 10.4.0 by Arcenox LLC, taken at
commit c6ed1e7, when TickerQ was MIT / Apache-2.0; 1.1 adds reliability fixes adapted from
TickerQ commits up to b2aefd0, still under those licences. Tickwork keeps the scheduler, the EF Core
provider, the source generator and the OpenTelemetry instrumentation; it does not carry the
dashboard, the Redis provider or the hub. Tickwork is not affiliated with or endorsed by Arcenox
LLC or the TickerQ project. See NOTICE.
Packages
| Package | What it is |
|---|---|
Tickwork |
The scheduler, its hosted services and the source generator (shipped inside it as an analyzer) |
Tickwork.Utilities |
The entities, managers and contracts the other packages share |
Tickwork.EntityFrameworkCore |
Persistence in your database through EF Core: PostgreSQL, SQL Server, SQLite, MySQL |
Tickwork.Instrumentation.OpenTelemetry |
OpenTelemetry traces for every job |
All packages are versioned together; keep them on the same version.
Install
dotnet add package Tickwork
dotnet add package Tickwork.EntityFrameworkCore # to keep jobs in your database
A minimal example
using Tickwork.DependencyInjection;
using Tickwork.Utilities.Base;
using Tickwork.Utilities.Entities;
using Tickwork.Utilities.Interfaces.Managers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTickwork(); // in memory; see below for a database
var app = builder.Build();
app.UseTickwork();
app.MapPost("/remind", async (ITimeTickerManager<TimeTickerEntity> tickers) =>
await tickers.AddAsync(new TimeTickerEntity
{
Function = "SendReminder",
ExecutionTime = DateTime.UtcNow.AddMinutes(5)
}));
app.Run();
public class Jobs
{
[TickerFunction("SendReminder")]
public Task SendReminder(TickerFunctionContext context, CancellationToken cancellationToken)
{
Console.WriteLine($"Reminder {context.Id}");
return Task.CompletedTask;
}
[TickerFunction("Cleanup", "0 3 * * *")] // every day at 03:00
public Task Cleanup(CancellationToken cancellationToken) => Task.CompletedTask;
}
UseTickwork extends IHost, so a worker or console app on the generic host calls it the same way:
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddTickwork();
var host = builder.Build();
host.UseTickwork();
await host.RunAsync();
To keep jobs in a database, add the EF Core store. With Tickwork's own context:
using Tickwork.EntityFrameworkCore.DbContextFactory;
using Tickwork.EntityFrameworkCore.DependencyInjection;
builder.Services.AddTickwork(options =>
options.AddOperationalStore(ef =>
ef.UseTickworkDbContext<TickworkDbContext>(db => db.UseNpgsql(connectionString))));
or inside your application's own DbContext, with
ef.UseApplicationDbContext<AppDbContext>(ConfigurationType.UseModelCustomizer). The tables live
in the ticker schema unless you set another with SetSchema. SQLite has no schemas: there EF Core
ignores the schema and logs warning 30000 (SchemaConfiguredWarning) for each table, which is
expected and harmless.
Moving from TickerQ 10.4
Tickwork 1.0 is TickerQ 10.4.0 under another name, so moving is a rename:
- Replace the packages:
TickerQ→Tickwork,TickerQ.Utilities→Tickwork.Utilities,TickerQ.EntityFrameworkCore→Tickwork.EntityFrameworkCore,TickerQ.Instrumentation.OpenTelemetry→Tickwork.Instrumentation.OpenTelemetry. - Replace
TickerQwithTickworkin yourusingdirectives. - Rename the few identifiers that carry the brand:
AddTickerQ→AddTickwork,UseTickerQ→UseTickwork,TickerQDbContext→TickworkDbContext,UseTickerQDbContext→UseTickworkDbContext,ITickerQHostScheduler→ITickworkHostScheduler,TickerQStartMode→TickworkStartMode,[TickerQConstructor]→[TickworkConstructor],TickerQEfCoreOptionBuilder→TickworkEfCoreOptionBuilder. Everything named Ticker (TimeTickerEntity,CronTickerEntity,[TickerFunction],TickerFunctionContext,TickerStatus, ...) keeps its name.
The database does not change. The schema, tables, columns, indexes and stored enum values are
the ones TickerQ 10.4.0 created, so dotnet ef migrations add after the upgrade produces an empty
migration (the model snapshot is rewritten with the new type names, which is expected). The tests
hold the model to TickerQ 10.4.0's snapshots on PostgreSQL, SQL Server and SQLite.
What changes for an operator: OpenTelemetry spans now come from the Tickwork activity source and
their tags start with tickwork. instead of tickerq., and log messages say Tickwork.
Building and testing
dotnet build Tickwork.slnx
dotnet test Tickwork.slnx
The SDK is pinned by global.json (a .NET 11 SDK builds both targets). The tests run on both
targets. The tests in the Category=Database trait start PostgreSQL and SQL Server in containers
through Testcontainers, so they need Docker or Podman; --filter "Category!=Database" leaves them out.
Releasing
Packages go to nuget.org through Trusted Publishing: there is no stored API key. Publishing a
GitHub release runs .github/workflows/release.yml, which builds, tests and packs both targets,
exchanges the workflow's OIDC token for a one-hour nuget.org key, and pushes the packages and their
symbols.
It needs, once:
- a trusted publishing policy on nuget.org for this repository and the workflow file
release.yml; - a GitHub environment named
release, which the publishing job runs in; - a repository secret
NUGET_USER: the nuget.org profile name (not the e-mail address) that owns the policy.
The version is <Version> in Directory.Build.props; tag the release v<version>.
License
MIT. Copyright (c) 2025-present Arcenox LLC, copyright (c) 2026 Xavier Klausener. See LICENSE and NOTICE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. 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. net11.0 is compatible. |
-
net10.0
- Tickwork.Utilities (>= 1.1.1)
-
net11.0
- Tickwork.Utilities (>= 1.1.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
1.1.1 - fixes a 1.1.0 regression in stopping the host. The scheduler's StopAsync no longer throws ObjectDisposedException when the host is stopped after it was disposed (as a WebApplicationFactory harness does at teardown), and tolerates being called more than once and concurrently. A stop whose token is cancelled still freezes the scheduler and stops the loop instead of throwing. Disposing while the loop is acquiring tickers no longer reports an ObjectDisposedException as a host exception. 1.1.0 -reliability fixes, same database schema. A ticker the fallback took over while it was queued is no longer run a second time (Queued to InProgress is a compare-and-set on the node's lock). Stopping the host no longer freezes the task scheduler between acquiring tickers and queueing them. A cancellation request no longer disposes the source of the execution still running, and an ending execution no longer removes a newer registration of the same ticker. Requests persisted before compression was switched on or off are read either way. The EF Core queries that load children and grandchildren run as one query (AsSingleQuery), which removes warning 20504. README: the generic host's UseTickwork, and the SQLite schema warning. Adapted from TickerQ fixes up to b2aefd0.