BitzOrcas runs background jobs in a separate JobHost process using Quartz.NET. This keeps the API Host trim-safe and AOT-compatible.
Architecture
┌──────────────────┐ ┌──────────────────┐│ API Host │ │ JobHost ││ (AOT, trimmed) │ │ (untrimmed) ││ │ │ ││ No Quartz │ │ Quartz.NET ││ No CAP consumer │ │ SqlSugar audit │└────────┬─────────┘ └────────┬─────────┘ │ │ └────────────┬───────────┘ ▼ SQL Server (shared DB)Registered jobs
| Job | Trigger | Purpose |
|---|---|---|
AuditRetentionQuartzJob | Cron (configurable) | Cleanup old audit records per retention policy |
Quartz configuration
{ "Audit": { "Retention": { "Enabled": true, "CronExpression": "0 0 2 * * ?", "MaxAgeDays": 90 } }}JobHost composition
The JobHost is minimal — only what’s needed for scheduled work:
Host.CreateApplicationBuilder(args) // Clock + Audit pipeline .AddBitzOrcasAuditDefaults() .AddBitzOrcasAuditPipeline() // SqlSugar (if connection configured) .AddBitzOrcasSqlSugar() .AddBitzOrcasSqlSugarAuditStore() // Quartz scheduling .AddQuartz(configure) .AddQuartzHostedService() // OTel + Health Checks .AddServiceDefaults()Tenant context for jobs
Background jobs have no HTTP context. For tenant-scoped operations:
await currentUserAccessor.BeginScopeAsync(userId, tenantId, async () =>{ // This code runs as the specified user/tenant});Adding a new job
- Create a class implementing
IJobinBitzOrcas.JobHost/ - Register in
Program.cs:
q.AddJob<MyNewJob>(opts => opts.WithIdentity("my-job"));q.AddTrigger(opts => opts .ForJob("my-job") .WithCronSchedule("0 0 */6 * * ?")); // Every 6 hoursSee also
- Jobs building block — Detailed infrastructure reference