Hangfire – Background Jobs für .NET und .NET Core Anwendungen
Viele Anwendungen benötigen Hintergrundjobs, die unabhängig von der Benutzeroberfläche ausgeführt werden können, also ohne dass ein Benutzer interagiert. Früher wurde dies häufig mit Windows Services gelöst.
Für .NET und .NET Core Anwendungen gibt es allerdings eine bessere Lösung: Hangfire (https://www.hangfire.io/)
Hangfire ist ein Open Source Framework um Background-Jobs zu erstellen und zu verwalten. Diese werden schließlich in der Datenbank gespeichert um zu gewährleisten, dass Jobs auch bei einem Neustart der Anwendung oder des Servers bestehen bleiben. Folgende Datenbank Speichersysteme werden unterstützt:
- Microsoft SQL Server
- Redis
- mongoDB
- PostgreSQL
- C1 CMS
Installation über die Package-Manager-Console
Install-Package Hangfire.Core
Install-Package Hangfire.SqlServer
Install-Package Hangfire.AspNet
Install-Package Hangfire.AspNetCore
dotnet add package Hangfire.Core
dotnet add package Hangfire.SqlServer
dotnet add package Hangfire.AspNet
dotnet add package Hangfire.AspNetCore
Konfiguration
Zunächst erstellst du eine Datenbank, sofern noch keine vorhanden und definierst den Connection String in der web.config Datei um eine Verbindung zwischen WebAPI und Datenbank zu gewährleisten. Anschließend musst du in der startup.cs Datei deines .NET oder .NET Core Projekts eine Verbindung zwischen Hangfire und Ihrer Datenbank herstellen.
Sample ASP.NET Core Startup class
---------------------------------
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Hangfire;
namespace MyWebApplication
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHangfire(x => x.UseSqlServerStorage("DefaultConnection"));
services.AddHangfireServer();
}
public void Configure(IApplicationBuilder app)
{
app.UseHangfireDashboard();
}
}
}
Sample OWIN Startup class
-------------------------
using Hangfire;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(MyWebApplication.Startup))]
namespace MyWebApplication
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
GlobalConfiguration.Configuration.UseSqlServerStorage("DefaultConnection");
app.UseHangfireDashboard();
app.UseHangfireServer();
}
}
}
Erstellung von Hintergrundjobs
Hangfire bietet uns verschiedene Methoden an, die zur Erstellung unterschiedlicher Hintergrundjobs dienen.
Zum einen gibt es die sogenannten Fire-and-forget jobs, die unmittelbar nach der Erstellung des Projekts ausgeführt werden.
var jobId = BackgroundJob.Enqueue(
() => Console.WriteLine("Fire-and-forget!"));
Delayed jobs werden ebenso einmalig ausgeführt, allerdings erst nach einem angegebenen Zeitintervall.
var jobId = BackgroundJob.Schedule(
() => Console.WriteLine("Delayed!"),
TimeSpan.FromDays(7));
Recurring jobs, werden immer wieder ausgeführt (z.B. stündlich, täglich, wöchentlich uvm.)
RecurringJob.AddOrUpdate(
() => Console.WriteLine("Recurring!"),
Cron.Daily);
Continuations jobs, werden ausgeführt, wenn der Vorläufige Job beendet wurde.
BackgroundJob.ContinueWith(
jobId,
() => Console.WriteLine("Continuation!"));
Beispiel - Statusänderung
Im folgende Beispiel wird die Funktion ChangeStatus() minütlich ausgeführt.
RecurringJob.AddOrUpdate(() => ChangeStatus(), Cron.Minutely);
Die Funktion ChangeStatus() setzt den Status einer Umfrage auf „Aktiv“ sobald das Startdatum überschritten wurde und die Umfrage nicht bereits den Status „Aktiv“ hat.
Anschließend werden die Daten in der Datenbank gespeichert.
public void ChangeStatus()
{
var today = DateTime.Now;
try
{
var list = new List();
using (var db = new TeamCheckEntities())
{
var surveysToOpen = db.Surveys.Where(s => s.Startdate <= today && s.Status.Name != DbConstants.StatusActive
&& s.Status.Name != DbConstants.StatusClosed);
var surveysToClose = db.Surveys.Where(s => s.Enddate <= today && s.Status.Name != DbConstants.StatusClosed);
var statusActive = db.Status.First(s => s.Name == DbConstants.StatusActive);
var statusClosed = db.Status.First(s => s.Name == DbConstants.StatusClosed);
foreach (var survey in surveysToOpen)
{
survey.Status = statusActive;
}
foreach (var survey in surveysToClose)
{
survey.Status = statusClosed;
}
db.SaveChanges();
}
}
catch (Exception e)
{
throw;
}
}