▲ 58 r/dotnet

NaturalCron 1.1.0: define your own schedule keywords

I introduced a new cool feature in NaturalCron 1.1.0: you can now customise your own keyword rule and add it to the NaturalCron engine. It is usually added once on app startup, before any parsing.

For those who don't know, NaturalCron is a scheduling library for .NET where you write schedules in plain words instead of cron strings, e.g. every 30 minutes between 09:00 and 18:00.

Version 1.1.0 adds custom keywords: you define a word once at app startup, with your own logic, and then use it in any expression.

NaturalCronCustomKeywords.Configure(cfg =>
{
    // A day keyword (the default time unit is Day).
    cfg.AddMatchable("holiday", dt => myHolidays.Contains(dt.Date));

    // An hour keyword.
    cfg.AddMatchable("evenHour", dt => dt.Hour % 2 == 0, NaturalCronTimeUnit.Hour);
});

Then:

every holiday at 09:00
every day at 09:00 except holidays
every day on [holiday, 15th] at 09:00

A keyword can also be a class, so it can come from your DI container and read from a cache.

Also new in 1.1.0:

  • except, which works with built-in values too: every day at 09:00 except [saturday, sunday]
  • A shorthand for every: every 15th at 18:00, every 1stMonday at 09:00, every 9am

GitHub: https://github.com/hugoj0s3/NaturalCron NuGet: https://www.nuget.org/packages/NaturalCron

Feedback welcome, especially on the syntax.

reddit.com
u/West_Ad6277 — 4 days ago

JobMaster: a .NET job scheduler that splits execution from the audit log to avoid row locks

I built JobMaster, a job scheduler for .NET aimed at horizontal scale. Execution is split from the audit trail and the coordination, so workers are not all locking the same rows.

Jobs go into a per-worker bucket first. The Master DB keeps the history. A job due soon stays in the bucket. A job due later is parked there and moved back in a batch when it is close. On a 50k burst with 20 workers, NATS in front of RavenDB did 23.8k jobs/sec, against 6.5k when one database did both.

I would appreciate any feedback, especially on the bucket split.

reddit.com
u/West_Ad6277 — 11 days ago
▲ 2 r/csharp

Voice skill interviews in .NET, and why the model can't keep time

I built a Blazor voice interviewer: pick a skill from JSON, talk, get a score. OpenAI via Microsoft.Extensions.AI, Azure Speech, audio on its own WebSocket.

One thing that surprised me: the model has no sense of time. It only sees the text in the conversation, so it would end the interview early. I had to send the remaining time in every interaction.

full article: Building a Skill Interview with AI

GitHub code: hugoj0s3/SkillsValidator

reddit.com
u/West_Ad6277 — 12 days ago
▲ 1 r/csharp

[Showcase] Added RavenDB support to JobMaster, a distributed background job scheduler for .NET

JobMaster is a distributed background job scheduler for .NET I've been building (think Hangfire/Quartz, but built for horizontal scaling). RavenDB is my favourite database, so I added it as a fully supported provider alongside PostgreSQL, MySQL, and SQL Server.

If you want the architecture background: https://docs.jobmaster.hugoj0s3.dev/docs/architecture-under-the-hood/architecture-overview

I also just finished a head-to-head benchmark against Hangfire across all four database engines. One result that stood out: RavenDB gets noticeably better scheduling throughput than the SQL engines (~2000 jobs/sec vs ~1300-1600/sec at baseline, on a 25k-job burst). Full methodology and numbers here: https://docs.jobmaster.hugoj0s3.dev/docs/benchmarks/jobmaster-vs-hangfire

GitHub: https://github.com/hugoj0s3/jobmaster-net

Happy to answer questions about the architecture, the RavenDB integration, or the benchmark setup.

u/West_Ad6277 — 1 month ago
▲ 0 r/dotnet

I built a .NET library to convert human schedules to classic Cron. Ships with Quartz, Cronos and NCrontab presets, but configurable for other formats

Hey everyone, a while back I shared NaturalCron here — a .NET scheduling library that lets you define recurrence rules in a more natural, human-readable way instead of memorizing cryptic cron strings.

I created new package to convert to the classic cron expression to easly integrate into an existing scheduler, a CI system, or Quartz.NET.

NaturalCron.CronConverter, a new optional add-on:

dotnet add package NaturalCron.CronConverter

var expr = NaturalCronExpr.Parse("every day at 18:00");
expr.ToCronExpression();                                       // "0 18 * * *"

// Or straight from the fluent builder
NaturalCronBuilder.Every(30).Minutes().ToCronExpression();     // "*/30 * * * *"

It ships with ready-made presets options for the three most popular .NET cron libraries no need to figure out field formats or DOW numbering yourself:

expr.ToCronExpression(CronConverterOptions.ForCronos());    // 5-field, Cronos
expr.ToCronExpression(CronConverterOptions.ForCrontab());   // 5-field, NCrontab
expr.ToCronExpression(CronConverterOptions.ForQuartz());    // 6-field, Quartz.NET

Some NaturalCron features have no classic cron equivalent things like time-window ranges, timezones, and yearly rules. These are all documented in the converter docs.

You can control the behavior via NonConvertibleBehavior either throw an exception or silently omit the incompatible parts.

Links

  • NuGet: NaturalCron / NaturalCron.CronConverter
  • GitHub
  • Converter docs including the full list of unsupported features
u/West_Ad6277 — 4 months ago
▲ 12 r/csharp

[Showcase] Added a monitoring UI to JobMaster, a open-source distributed job engine for .NET.

Hey

A few months ago, I shared my open-source job schedule engine, JobMaster, here. Since then, I've been working on the monitoring dashboard (built with Svelte!), and it's finally in a usable state. I wanted to share it with you all.

https://preview.redd.it/rpb0p1jl7y6h1.png?width=1080&format=png&auto=webp&s=98eec06ea56974f7451775014ea6c0d517526228

What the dashboard gives you:

  • Overview: Workers online, upcoming executions, and failed jobs.
  • Job Browser: Filterable by status and time range, with full details per job (handler logs, retry history with error messages, payload, and metadata).
  • Worker & Bucket Visibility: See exactly which workers are running, their mode, and the specific jobs inside their buckets.
  • Host Monitoring: CPU, memory, and uptime per node.
  • Agent Connection Health: Transport liveness and last heartbeat.

One detail you can customize the color theme per cluster, so production and QA are visually distinct. It’s a small thing, but it saves that "wait, which environment am I in?" when you have multiple tabs open.

It uses DaisyUI and Tailwind CSS

Minimal setup:

builder.Services.AddJobMasterDashboard(dashboard =>
{
    dashboard.UseBasePath("/jm-dashboard");
    dashboard.FromOpenApiJson(); // auto-discovers clusters + auth from the API spec
});
app.StartJobMasterDashboard();

Links & Resources:

GitHub: https://github.com/hugoj0s3/jobmaster-net

Would love to hear your thoughts or any feedback if you give it a spin!

reddit.com
u/West_Ad6277 — 4 months ago
▲ 24 r/dotnet

[Showcase] Added a monitoring dashboard to JobMaster, my distributed .NET job engine

Hey r/dotnet,

A few months ago, I shared my open-source job schedule engine, JobMaster, here. Since then, I've been working on the monitoring dashboard (built with Svelte!), and it's finally in a usable state. I wanted to share it with you all.

https://preview.redd.it/ryiw4rou4o5h1.png?width=1866&format=png&auto=webp&s=ceabf7b666fd5b61fe8de499a6ac9669517310c5

What the dashboard gives you:

  • Overview: Workers online, upcoming executions, and failed jobs.
  • Job Browser: Filterable by status and time range, with full details per job (handler logs, retry history with error messages, payload, and metadata).
  • Worker & Bucket Visibility: See exactly which workers are running, their mode, and the specific jobs inside their buckets.
  • Host Monitoring: CPU, memory, and uptime per node.
  • Agent Connection Health: Transport liveness and last heartbeat.

One detail you can customize the color theme per cluster, so production and QA are visually distinct. It’s a small thing, but it saves that "wait, which environment am I in?" panic moment when you have multiple tabs open.

It uses DaisyUI and Tailwind CSS

Minimal setup:

builder.Services.AddJobMasterDashboard(dashboard =>
{
    dashboard.UseBasePath("/jm-dashboard");
    dashboard.FromOpenApiJson(); // auto-discovers clusters + auth from the API spec
});
app.StartJobMasterDashboard();

Links & Resources:

GitHub: https://github.com/hugoj0s3/jobmaster-net

Would love to hear your thoughts or any feedback if you give it a spin!

reddit.com
u/West_Ad6277 — 4 months ago
▲ 5 r/csharp

I built a cron converter add-on for NaturalCron — converts human-readable schedules to classic cron strings (Cronos, NCrontab, Quartz.NET)

Hey everyone, a while back I shared NaturalCron here — a .NET scheduling library that lets you define recurrence rules in a more natural, human-readable way instead of memorizing cryptic cron strings.

What's new

The main ask I kept getting was: "Can I get a classic cron string out of it?" — maybe to plug into an existing scheduler, a CI system, or Quartz.NET.

So I built NaturalCron.CronConverter, a new optional add-on:

dotnet add package NaturalCron.CronConverter

var expr = NaturalCronExpr.Parse("every day at 18:00");
expr.ToCronExpression();                                       // "0 18 * * *"

// Or straight from the fluent builder
NaturalCronBuilder.Every(30).Minutes().ToCronExpression();     // "*/30 * * * *"

It ships with ready-made presets options for the three most popular .NET cron libraries — no need to figure out field formats or DOW numbering yourself:

expr.ToCronExpression(CronConverterOptions.ForCronos());    // 5-field, Cronos
expr.ToCronExpression(CronConverterOptions.ForCrontab());   // 5-field, NCrontab
expr.ToCronExpression(CronConverterOptions.ForQuartz());    // 6-field, Quartz.NET

Some NaturalCron features have no classic cron equivalent — things like time-window ranges, timezones, and yearly rules. These are all documented in the converter docs. You can control the behavior via NonConvertibleBehavior — either throw an exception or silently omit the incompatible parts.

Also in v1.0.1

Renamed ToRawExpression() → ToNaturalExpression() on the fluent builder. The old name confused people into thinking it returned a cron string — fair point, fixed with a deprecation notice. Issue

Links

  • NuGet: NaturalCron / NaturalCron.CronConverter
  • GitHub
  • Converter docs — including the full list of unsupported features
reddit.com
u/West_Ad6277 — 4 months ago