NCronJob - June Edition

6/21/2024
6 minute read

The last update of NCronJob was some time ago - and as always, there are some new features in the meantime. So here we are, let's go through to highlight them!

If you are not familiar with what NCronJob is, here are three links:

Now that we are warm and ready, let's dive into the new features!

Dependent Jobs

I teased this already in the last blog post, but now it is up and running! You can now define dependent jobs, which will be executed after the parent job has finished.

flowchart

It is easily done by adding the ExecuteWhen chain:

Services.AddNCronJob(options => 
{
    options.AddJob<JobB>().ExecuteWhen(
        success: s => s.RunJob<SuccessJob>(),
        faulted: f => f.RunJob<FaultedJob>());
});

You could even chain multiple jobs together:

Services.AddNCronJob(options => 
{
    options.AddJob<JobB>().ExecuteWhen(
        success: s => s.RunJob<SuccessJob>().RunJob<AnotherJob>(),
        faulted: f => f.RunJob<FaultedJob>());
});

A dependent job has access to the output of the parent:

public class JobA : IJob
{
    public Task ExecuteAsync(JobExecutionContext context)
    {
        context.Output = "Hello World";
        return Task.CompletedTask;
    }
}

public class JobB : IJob
{
    public Task ExecuteAsync(JobExecutionContext context)
    {
        var parentOutput = context.ParentOutput; // "Hello World"
        return Task.CompletedTask;
    }
}

// Register the dependency so that JobB runs after JobA automatically
Services.AddNCronJob(options => 
{
    options.AddJob<JobA>().ExecuteWhen(success: s => s.RunJob<JobB>());
});

A parent job also has the ability to cancel the dependent job:

public class JobA : IJob
{
    public Task ExecuteAsync(JobExecutionContext context)
    {
        context.SkipChildren();
        return Task.CompletedTask;
    }
}

Minimal API everywhere

We introduce a new "model" that leans towards the Minimal API of ASP.NET itself. You can easily define "just" lambdas instead of defining a whole class that derives from IJob. We allowed this behavior almost everywhere:

Job Registration

builder.Services.AddNCronJob((IMyService service, JobExecutionContext context, CancellationToken token) =>
{
    service.DoSomething();
}, "*/5 * * * * *");

Also, the JobExecutionContext is passed in as well as the CancellationToken - so you can easily cancel the job if needed.

Instant Jobs

app.MapPost("/send-email", (RequestDto dto, IInstantJobRegistry jobRegistry) => 
{
    var parameterDto = new ParameterDto
    {
        Email = dto.Email,
        Subject = dto.Subject,
        Body = dto.Body
    };

    jobRegistry.RunInstantJob<MyJob>(parameterDto);
    return Results.Ok();
});

Here we used the Minimal API from ASP.NET to trigger an instant job via our Minimal API.

Dependent Jobs

builder.Services.AddNCronJob(options => 
{
    options.AddJob<ImportDataJob>().ExecuteWhen(
        success: s => s.RunJob(async (ITransfomerService transformerService) => await transformerService.TransformDataAsync()),
        faulted: f => f.RunJob(async (INotificationService notificationService) => await notificationService.SendNotificationAsync()));
});

Of course, the same can be done with the Minimal API!

Startup Jobs

We introduced a new concept called "Startup Jobs". These jobs are executed once when the application starts. This is useful for jobs that need to run once when the application starts, like seeding a database or loading some configuration. They will only run once and every other job has to wait until they are finished. We block because you might rely on the startup job to be finished (like a DB Migration).

public class MyStartupJob : IJob
{
    public Task RunAsync(JobExecutionContext context, CancellationToken token)
    {
        // Perform startup task
        return Task.CompletedTask;
    }
}

Services.AddNCronJob(options => 
{
    options.AddJob<MyStartupJob>()
           .RunAtStartup();
});

Adding, removing or updating jobs at runtime

You can now add or remove jobs at runtime. This is useful if you want to add a job based on a user's input or remove a job that is no longer needed. You can also update the schedule of a job at runtime. For that we introduced the option to name a schedule (basically a combination of job, cron expression and parameter).

app.MapPost("/add-job", (IRuntimeJobRegistry registry) => 
{
    registry.AddJob(n => n.AddJob<SampleJob>(p => p.WithCronExpression("* * * * *").WithName("MyName")));
    return TypedResults.Ok();
});

The outer AddJob method has the same signature as AddNCronJob - so you get the same builder with the same options! Nothing new to learn here.

app.MapDelete("/remove-job", (IRuntimeJobRegistry registry) => 
{
    registry.RemoveJob("MyName");
    return TypedResults.Ok();
});

Also by job type is possible:

app.MapDelete("/remove-job", (IRuntimeJobRegistry registry) => 
{
    registry.RemoveJob<SampleJob>(); // Alternatively RemoveJob(typeof(SampleJob))
    return TypedResults.Ok();
});

And here the update case:

app.MapPut("/update-job", (IRuntimeJobRegistry registry) => 
{
    registry.UpdateSchedule("MyName", "* * * * *", TimeZoneInfo.Utc);
    return TypedResults.Ok();
});

Smaller but noteworthy things

Besides those big changes, there are some smaller but noteworthy things:

  • We moved the code from LinkDotNet.NCronJob to NCronJob - so our own Organization without my prefix. It didn't really fit and now it is gone!
  • We have the whole documentation live under https://docs.ncronjob.dev/ - so you can easily access it!

Conclusion

NCronJob is somewhat young, but we are confident that it is a great library for scheduling jobs. We are already seeing usage and more importantly big differences to Hangfire or Quartz. Mainly this library should feel very very close to ASP.NET! The way you would configure or setup things is just "ASP".

You can leave a star on our repository - that is always appreciated! Furthermore, we introduced also devcontainers for the repository - so you can easily start developing without any hassle. Here is the quick link:

Open in GitHub Codespaces

This is used for the development of the code or the documentation (which is of course also hosted in the same repository).

NCronJob - Big Updates

Since the last blog post a lot has happened and many new features have been added to NCronJob. In this blog post I would like to introduce you to the new features and explain how you can use them.

.NET 8 and Blazor United / Server-side rendering

New .NET and new Blazor features. In this blog post, I want to highlight the new features that are hitting us with .NET 8 in the Blazor world. So let's see what's new.

NCronJob - Scheduling made easy

Hangfire/Quartz or BackgroundService? Why not something in the middle? Did you ask yourself this question from time to time? Do you want to have a full-blown job scheduler with lots of setups, but more than BackgroundService is needed?

Meet: NCronJob!

An error has occurred. This application may no longer respond until reloaded. Reload x