Hangfire - Setup and Test - Code

C#

Program.cs

using BlazorBirthdayReminders.Data;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Syncfusion.Blazor;
using Hangfire;
using Hangfire.SqlServer;



var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddControllersWithViews()
    .AddMicrosoftIdentityUI();

builder.Services.AddAuthorization(options =>
{
    // By default, all incoming requests will be authorized according to the default policy
    options.FallbackPolicy = options.DefaultPolicy;
});

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor()
    .AddMicrosoftIdentityConsentHandler();
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddScoped<IPersonService, PersonService>();
//builder.Services.AddScoped<IEmailService, EmailService>();
builder.Services.AddTransient<IEmailService, EmailService>();           //Scoped didn't seem to work. Singleton did work.

builder.Services.AddSyncfusionBlazor(options => { options.IgnoreScriptIsolation = true; });

builder.Services.AddHangfire(x => x
    .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
    .UseSimpleAssemblyNameTypeSerializer()
    .UseRecommendedSerializerSettings()
    .UseSqlServerStorage(builder.Configuration.GetConnectionString("Default"), new SqlServerStorageOptions
    {
        CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
        SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
        QueuePollInterval = TimeSpan.Zero,
        UseRecommendedIsolationLevel = true,
        DisableGlobalLocks = true
    }));

builder.Services.AddHangfireServer();


var app = builder.Build();

///Register Syncfusion license
var SyncfusionLicenceKey = builder.Configuration["SyncfusionLicenceKey"];
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense(SyncfusionLicenceKey);

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.UseHangfireDashboard("/dashboard");

IEmailService EmailService = app.Services.GetRequiredService<IEmailService>();      //Needed for items below

BackgroundJob.Enqueue(() => EmailService.Send("christopherbell@blazorcode.uk",
    "SUBJECT - The program has started",
    "This is the body of the email.  This is the email to say the program has started"));

BackgroundJob.Schedule(() => EmailService.Send("christopherbell@blazorcode.uk",
    "This is sent after a minute"
    , "Welcome - This was sent one minute after starting"),
    TimeSpan.FromMinutes(1));

RecurringJob.AddOrUpdate(
    "Run every 5 minutes", () => EmailService.Send("christopherbell@blazorcode.uk",
     "Recurring Email every 5 minutes",
     "Another 5 minutes ticks by...."),
    "*/5 * * * *");

RecurringJob.AddOrUpdate(
    "Run every day at 10:57", () => EmailService.Send("christopherbell@blazorcode.uk",
     "Recurring Email every day at 10:57",
     "This is the body of the email"),
     "57 10 * * *");


app.Run();
<div class="top-row ps-3 navbar navbar-dark">
    <div class="container-fluid">
        <a class="navbar-brand" href="">BlazorBirthdayReminders</a>
        <button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
            <span class="navbar-toggler-icon"></span>
        </button>
    </div>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <nav class="flex-column">
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </div>
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </div>
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
            </NavLink>
        </div>
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="emailtest">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Email Test
            </NavLink>
        </div> 
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="dashboard">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Hangfire Dashboard
            </NavLink>
        </div> 
    </nav>
</div>

@code {
    private bool collapseNavMenu = true;

    private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}