如何将 SignalR hub 上下文传递给 ASP .NET Core 2.1 上的 Hangfire 作业?

How can I pass a SignalR hub context to a Hangfire job on ASP .NET Core 2.1?

如何将 SignalR hub 上下文传递给 ASP .NET Core 2.1 上的 Hangfire 作业?

似乎由于向 Hangfire 传递参数是通过 serialization/deserialization 完成的,Hangfire 似乎很难重建 SignalR hub 上下文。

我安排工作(在我的控制器中)使用:

BackgroundJob.Schedule(() => _hubContext.Clients.All.SendAsync(
        "MyMessage",
        "MyMessageContent", 
        System.Threading.CancellationToken.None), 
    TimeSpan.FromMinutes(2));

然后 2 分钟后,当作业尝试执行时,出现错误:

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.

有什么想法吗?

更新 1

我最终使用了在 Startup.cs 中定义并从 Configure()

分配的静态上下文
hbctx = app.ApplicationServices.GetRequiredService<IHubContext<MySignalRHub>>(); 

所以现在 Hangfire 安排了一个使用静态上下文的集线器助手:

BackgroundJob.Schedule(() => new MyHubHelper().Send(), TimeSpan.FromMinutes(2)); 

并且集线器助手通过 Startup.hbctx

获取上下文

尽管这有效,但有点臭

更新 2

我也尝试使用 中的方法:

我的后台作业调度变成了:

BackgroundJob.Schedule(() => Startup.GetService().SendOutAlert(2), TimeSpan.FromMinutes(2));

但是这次,当我到达上面一行时出现异常:

An unhandled exception has occurred while executing the request System.ObjectDisposedException: Cannot access a disposed object. Object name: 'IServiceProvider'.

更新 3

谢谢大家。解决方案是创建一个 helper,通过 DI 通过其构造函数获取 hubcontext,然后使用 hangfire 调度 helper 方法 Send 作为后台作业。

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");
    }
}

然后使用 :

从任何地方启动后台作业
BackgroundJob.Schedule<MyHubHelper>( x => x.SendOutAlert(userId), TimeSpan.FromMinutes(2));

使用 Schedule<T> 泛型,您应该能够利用框架的依赖注入功能。

BackgroundJob.Schedule<IHubContext<MySignalRHub>>(hubContext => 
    hubContext.Clients.All.SendAsync(
        "MyMessage",
        "MyMessageContent", 
        System.Threading.CancellationToken.None), 
    TimeSpan.FromMinutes(2));

Nkosi 的回答建议使用 Schedule<T> 泛型让我找到了我使用的最终解决方案:

首先,我的 MySignalRHub 只是一个空的 class 继承自 Hub。

public class MySignalRHub 
{
}

然后,我创建了一个集线器助手,它在我的 MySignalRHub 上维护一个集线器上下文。 hubcontext 通过 ASP.Net 核心内置 DI 机制(如 here 所解释的)注入助手 class。

帮手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);
    }
}

辅助界面:

public interface IMyHubHelper
{
    void SendData(String data);
}

最后,我可以使用 hangfire 从代码中的任何位置安排 hub 助手的方法 SendData() 作为后台作业:

BackgroundJob.Schedule<MyHubHelper>(h => h.SendData(myData), TimeSpan.FromMinutes(2));