如何将 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));
如何将 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));