Hangfire is one of the easiest ways to embed background processing in your .NET (Core) applications. It does all the hard work for you so you can focus on the application logic itself.
Scheduling a recurring job is as easy as using this oneliner:
RecurringJob.AddOrUpdate( | |
"myrecurringjob", | |
() => Console.WriteLine("Recurring!"), | |
Cron.Daily); |
As you can see background jobs in Hangfire look like regular method calls. Hangfire will collect and serialize the method information using the Newtonsoft.Json package and store the resulting JSON.
In real life applications I typically need to pass some arguments to the background job which is supported as well:
public class EmailSender | |
{ | |
public void Send(Message message) | |
{ | |
// Some processing logic | |
} | |
} |
public class Message { | |
public Message(string fromAddress) | |
{ | |
FromAddress = fromAddress; | |
} | |
public string FromAddress { get; private set; } | |
public string? ToAddress { get; private set; } | |
public void SetToAddress(string toAddress) | |
{ | |
ToAddress = toAddress; | |
} | |
} |
var message=new Message("from"); | |
message.SetToAddress("to"); | |
RecurringJob.AddOrUpdate<EmailSender>( | |
"myrecurringjob", | |
sender => sender.Send(message), | |
Cron.Daily); |
In the code above the Message
object is serialized as well. Although this is correctly serialized and stored by Hangfire, trouble arrives when this scheduled task is actually invoked. Hangfire will deserialize our Message
object but the private properties will remain Null
.
If you don’t mind having a dependency on Newtonsoft.Json, you can fix it by adding a [JsonProperty]
attribute:
public class Message { | |
public Message(string fromAddress) | |
{ | |
FromAddress = fromAddress; | |
} | |
public string FromAddress { get; private set; } | |
[JsonProperty] | |
public string? ToAddress { get; private set; } | |
public void SetToAddress(string toAddress) | |
{ | |
ToAddress = toAddress; | |
} | |
} |
Or the option that I prefer is to update the Hangfire serialization configuration: to support non public members:
var settings = new JsonSerializerSettings | |
{ | |
ContractResolver = new DefaultContractResolver | |
{ | |
DefaultMembersSearchFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | |
} | |
}; | |
GlobalConfiguration.Configuration.UseSerializerSettings(settings); |