How can I pass a SignalR hub context to a Hangfire job on ASP .NET Core 2.1?
It seems that since passing arguments to Hangfire is done via serialization/deserialization, it seems that Hangfire has hard-time reconstructing the SignalR hub context.
I schedule the job (in my controller) using :
BackgroundJob.Schedule(() => _hubContext.Clients.All.SendAsync(
        "MyMessage",
        "MyMessageContent", 
        System.Threading.CancellationToken.None), 
    TimeSpan.FromMinutes(2));
Then after 2 minutes, when the job tries to execute, I have the error :
Newtonsoft.Json.JsonSerializationException: Could not create an instance of type Microsoft.AspNetCore.SignalR.IClientProxy. Type is an interface or abstract class and cannot be instantiated.
Any idea?
Update 1
I ended up using a static context defined in Startup.cs, and assigned from Configure()
hbctx = app.ApplicationServices.GetRequiredService<IHubContext<MySignalRHub>>(); 
So now Hangfire schedules instead a hub helper that uses the static context :
BackgroundJob.Schedule(() => new MyHubHelper().Send(), TimeSpan.FromMinutes(2)); 
and the hub helper gets the context with Startup.hbctx
Even though this is working, it is a little smelly
Update 2
I tried also using the approach in Access SignalR Hub without Constructor Injection:
My background job scheduling became :
BackgroundJob.Schedule(() => Startup.GetService().SendOutAlert(2), TimeSpan.FromMinutes(2));
However this time, I have an exception when I reach the above line:
An unhandled exception has occurred while executing the request System.ObjectDisposedException: Cannot access a disposed object. Object name: 'IServiceProvider'.
Update 3
Thanks all. The solution was to create a helper that gets the hubcontext via its constructor via DI, and then using hangfire to schedule the helper method Send as the background job.
public interface IMyHubHelper
{
    void SendOutAlert(String userId);
}
public class MyHubHelper : IMyHubHelper
{
    private readonly IHubContext<MySignalRHub> _hubContext;
    public MyHubHelper(IHubContext<MySignalRHub> hubContext)
    {
        _hubContext = hubContext;
    }
    public void SendOutAlert(String userId)
    {
        _hubContext.Clients.All.SendAsync("ReceiveMessage", userId, "msg");
    }
}
Then launching the background job from anywhere with :
BackgroundJob.Schedule<MyHubHelper>( x => x.SendOutAlert(userId), TimeSpan.FromMinutes(2));
The answer from Nkosi suggesting to use Schedule<T> generics pointed me to the final solution I used:
First, my MySignalRHub is just an empty class inheriting from Hub.
public class MySignalRHub 
{
}
Then, I created a hub helper which maintains a hubcontext on my MySignalRHub. The hubcontext is injected in the helper class via the ASP.Net Core built-in DI mechanism (as explained here).
The helper class:
public class MyHubHelper : IMyHubHelper
{
    private readonly IHubContext<MySignalRHub> _hubContext;
    public MyHubHelper(IHubContext<MySignalRHub> hubContext)
    {
        _hubContext = hubContext;
    }
    public void SendData(String data)
    {
        _hubContext.Clients.All.SendAsync("ReceiveMessage", data);
    }
}
The helper interface:
public interface IMyHubHelper
{
    void SendData(String data);
}
Finally, I can use hangfire to schedule from anywhere in the code the method SendData() of the hub helper as a background job with:
BackgroundJob.Schedule<MyHubHelper>(h => h.SendData(myData), TimeSpan.FromMinutes(2));
Using Schedule<T> generics you should be able to take advantage of the dependency injection capabilities of the framework.
BackgroundJob.Schedule<IHubContext<MySignalRHub>>(hubContext => 
    hubContext.Clients.All.SendAsync(
        "MyMessage",
        "MyMessageContent", 
        System.Threading.CancellationToken.None), 
    TimeSpan.FromMinutes(2));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With