天道酬勤,学无止境

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

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

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 如何在ASP.NET Core中获取SignalR Hub上下文?(How to get SignalR Hub Context in a ASP.NET Core?)
    问题 我正在尝试使用以下命令获取中心的上下文: var hubContext = GlobalHost.ConnectionManager.GetHubContext<SomeHub>(); 问题是没有定义GlobalHost。 我看到它是SignalR.Core dll的一部分。 目前,我在项目.json文件中的依赖项中包含以下内容: "Microsoft.AspNet.SignalR.Server": "3.0.0-*" 如果我添加Core的最新可用版本: "Microsoft.AspNet.SignalR.Server": "3.0.0-*", "Microsoft.AspNet.SignalR.Core" : "2.1.2" 我收到很多错误,因为服务器和核心冲突。 如果我将它们都更改为都使用版本“ 3.0.0- *”,则所有冲突都将消失,但是找不到GlobalHost。 如果我删除服务器,而仅使用用户核心版本2.1.2,则GlobalHost可以工作,但是所有其他需要服务器的东西显然都不能。 有任何想法吗? 回答1 Microsoft.AspNet.SignalR.Infrastructure.IConnectionManager是一种DI注入服务,通过它可以获取集线器上下文...例如: using Microsoft.AspNet.SignalR; using
  • 请求线程外的ASP.NET Core RC2 SignalR集线器上下文(ASP.NET Core RC2 SignalR Hub Context outside request thread)
    问题 我目前正在尝试ASP.NET Core的RC2版本,并且遇到SignalR的问题。 我需要能够将消息发送到请求线程之外的客户端。 现在,在完整的.NET框架中,您可以像这样进行操作: var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>(); context.Clients.<SendMessage>() 但是在ASP.NET Core中没有GlobalHost 。 我发现了一个类似的问题:如何在vNext项目中获取SignalR Hub上下文? 第二个答案提供了一种在请求线程之外获取hubcontext的方法,但这在ASP.NET Core也不起作用。 所以我的问题是:如何在ASP.NET Core的请求范围之外获取集线器上下文? 回答1 您将必须从:Signalr Github中获取当前的github版本(在撰写本文时,提交: b95ac7b ) 一旦有了它,并加载了解决方案,或将所有三个项目添加到现有解决方案中,您将需要在所有三个项目中更改project.json。 Microsoft.AspNetCore.SignalR.Server-project.json 您将看到每个组件的版本1.1.0- *(RC3)的引用。 将它们更改为当前的RC2,直到看到以下内容 "Microsoft
  • ASP.NET Core RC2 SignalR Hub Context outside request thread
    I am currently trying out the RC2 release of ASP.NET Core and I am running into an issue with SignalR. I need to be able to send messages to the client outside of the request thread. Now in the full .NET framework you can do this like: var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>(); context.Clients.<SendMessage>() But in ASP.NET Core there is no GlobalHost. I found a similar question: How to get SignalR Hub Context in a vNext Project? Where the second answer provides a method to get the hubcontext outside the request thread, but this also does not work in ASP.NET Core. So my
  • How to get SignalR Hub Context in a ASP.NET Core?
    I'm trying to get the context for a hub using the following: var hubContext = GlobalHost.ConnectionManager.GetHubContext<SomeHub>(); The problem is that GlobalHost is not defined. I see it is part of the SignalR.Core dll. At the moment, I have the following in my project .json file, under dependencies: "Microsoft.AspNet.SignalR.Server": "3.0.0-*" If I add the latest available version of Core: "Microsoft.AspNet.SignalR.Server": "3.0.0-*", "Microsoft.AspNet.SignalR.Core" : "2.1.2" I get a whole bunch of errors because server and core are conflicting. If I change them to both use version "3.0.0-*
  • 在Typescript中使用ASP.NET Core SignalR和React创建实时应用程序
    目录 介绍 ScrumPoker应用程序 源代码 开发工具 基本步骤 后端代码 创建Hub 在Startup中注册集线器 创建持久性 让我们为客户端应用程序公开一些终端 启用Cors 前端代码 结论 SignalR现在包含在ASP.NET Core框架中,并且进行了大量改进,使其轻巧易用。令我惊讶的是,我找不到任何有关如何使用SignalR的好教程,并且无法使用它使相同的旧聊天应用程序变得有趣。我想到了用SignalR创建一些东西,而不是同一个无聊的聊天应用程序。 介绍 在本教程中,我将指导您完成创建实时应用程序所需的主要步骤。我不会在这里写完整的代码。您可以在github 上找到完整的源代码。 ScrumPoker应用程序 在本教程中,我们将创建一个有趣的应用程序,名为ScrumPoker。我们生活在敏捷的世界中,因此在我们的开发过程或每个冲刺周期中进行故事评估并指出要点很普遍。过去,我们曾经计划使用扑克牌,而团队则通过这些牌来进行故事评估,但是现在一切都在线上了,我们经常进行远程工作。 用户可以创建ScrumBoard链接并与队友共享链接。团队成员可以进入那里并开始指出故事。只有当创建的用户ScrumBoard允许他们查看时,团队给出的点才会显示在仪表板上。 用户会实时添加到仪表板上,他们提交的点也会实时反映出来。 源代码 ├───clientapp├───Contracts├
  • 如何在中心线之外使用SignalR集线器实例(How to use SignalR hub instance outside of the hubpipleline)
    问题 我正在使用SignalR向所有客户广播消息。 我需要在集线器类之外触发广播,例如以下内容: var broadcast = new chatHub(); broadcast.Send("Admin","stop the chat"); 我收到以下错误消息: 不支持使用不是由HubPipeline创建的Hub实例。 回答1 您需要使用GetHubContext : var context = GlobalHost.ConnectionManager.GetHubContext<chatHub>(); context.Clients.All.Send("Admin", "stop the chat"); 有关详细信息,请参见http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#callfromouthub。 回答2 对于那些可能想知道GlobalHost去哪里的人的一个小更新。 SignalR已针对.net核心进行了完全重写。 因此,如果您使用SignalR.Core包(SignalR版本之间的差异),则可以通过将SignalR集线器上下文注入服务中来获得一个实例: public class MyNeedyService { private readonly
  • 使用SignalR的WCF服务(WCF Service with SignalR)
    问题 我有一个Web应用程序,其仪表板上的图表很少。 图表的数据在客户端调用WCF服务方法的document.ready函数上获取。 我现在想要的是在我的应用程序中使用SignalR。 我真的是SignalR的新手。 我如何从SignalR Hub调用WCF方法,或者您能说的是,与其从服务器提取数据,我不希望WCF服务每隔一分钟将数据推送到客户端。 signalR和WCF服务之间是否有通信方式。 另一种方法可以是强制客户端每分钟从WCF服务请求数据。 任何帮助将不胜感激。 到目前为止,我已经完成了以下工作。 我的仪表板页面上的客户端功能 <script src="Scripts/jquery.signalR-2.0.3.min.js"></script> <!--Reference the autogenerated SignalR hub script. --> <script src="/signalr/hubs"></script> <a id="refresh">Refresh</a> $(function() { var dashboardHubProxy = $.connection.dashboardHub; $.connection.hub.start().done(function() { // dashboardHubProxy.server
  • 如何在Android中使用Signalr(How to use signalr in Android)
    问题 我正在尝试将signalR集成到android应用中,但是没有运气。 我一直在研究各种链接,但没有一个提供有关实现的适当信息。 我有以下问题。 SignalR集成必须在服务/意图服务内部完成吗? 如果我们想通过相同的调用方法接收响应,那么如何获得? 我添加了三个库,即signalr android , signalr client和gson但无法理解代码的工作原理,没有适当的文档可用来理解代码。 提出了一些问题,但没有太多信息 Android Studio中的SignalR无法使用Android中的SignalR实现p2p聊天 如果有人对本机应用程序的信号处理有经验,那对我会非常有帮助。 更新 public class SignalRService extends Service { private static final String TAG = "Service"; private HubConnection mHubConnection; private HubProxy mHubProxy; private Handler mHandler; // to display Toast message private final IBinder mBinder = new LocalBinder(); private SharedPreferences sp;
  • How to use shared SignalR Hub from different hosted project in ASP.NET Core 2.2
    I'm working with a project built with ASP.NET Core 2.2. The main solution contains multiple projects, which includes API, web and other class libraries. We've used SignalR to displaying shared messages/notifications between the API project and the web project. For example, adding a new employee record from an API should call SignalR Hub and all the web client should received the notification. Here is the current structure of our project |- API |- Hub_ClassLibrary |- Services |- Web Flow: Web > services > Hub API > services > Hub Hub: public class NotificationHub : Hub { public async Task
  • 通过http标头SignalR传递令牌(Passing token through http Headers SignalR)
    问题 我可以看到HubConnection中有一个选项,可以通过来自客户端的url请求传递参数。 有什么方法可以通过JS或.NET客户端的http标头传递特定令牌? 回答1 没有使用JS或.NET客户端为SignalR请求设置HTTP标头的简便方法,但是您可以在查询字符串中添加参数,这些参数将作为每个SignalR请求的一部分发送: JS客户端 $.connection.hub.qs = { "token" : tokenValue }; $.connection.hub.start().done(function() { /* ... */ }); .NET客户端 var connection = new HubConnection("http://foo/", new Dictionary<string, string> { { "token", tokenValue } }); 在集线器内,您可以通过上下文访问社区名称: Context.QueryString["token"] 进行持久连接时,也可以添加到查询字符串中。 编辑:现在可以在某些评论者指出的.NET SignalR客户端上设置标头。 在.NET客户端上设置标题 var connection = new HubConnection("http://foo/"); connection.Headers.Add(
  • SignalR,Owin和异常处理(SignalR, Owin and exception handling)
    问题 我已经开发了一个基于ASP.NET 4.5和Owin的SignalR应用程序示例,并且已将该应用程序托管在IIS 7.5上。 一切工作正常,但是如何在Owin中处理异常? 考虑以下代码: [HubName("SampleHub")] public class SampleHub : Hub { public SampleHub() { throw new InvalidOperationException("?!"); } } 此异常不会调用Application_Error (这是我的问题)。 我可以从Owin那里获取与Application_Error类似的用于日志记录和调试目的的所有异常吗? 我对这样的东西不感兴趣: app.UseErrorPage(new ErrorPageOptions() { ShowCookies = true, ShowEnvironment = true, ShowExceptionDetails = true, ShowHeaders = true, ShowQuery = true, ShowSourceCode = true }); 对于高级方案(例如ASP.NET Web API和ASP.NET MVC),这完全没有用。 具有用于覆盖目的的OnException方法的动作过滤器要好得多。 回答1
  • How to authorize SignalR Core Hub method with JWT
    I am using JWT authentication in my ASP.NET Core 2.0 application with OpenIddict. I am following idea in this thread and calling AuthorizeWithJWT method after SignalR handshake. But now, I do not know what should I set in AuthorizeWithJWT method so I can use [Authorize(Roles="Admin")] for example. I tried with setting context user, but it is readonly: public class BaseHub : Hub { public async Task AuthorizeWithJWT(string AccessToken) { //get user claims from AccesToken this.Context.User = user; //error User is read only } } And using authorize attribute: public class VarDesignImportHub
  • 如何在ASP.NET Web API中排队后台任务(How to queue background tasks in ASP.NET Web API)
    问题 我有一个旨在以队列方式处理报告的webapi。 该应用程序采取的步骤如下: 接收内容将内容映射到对象并将其放入队列轮询队列中的未决项目一次处理一个队列中的项目 我当时正在考虑使用Entity Framework创建一个排队项目的数据库,例如: public class EFBatchItem { [Key] public string BatchId { get; set; } public DateTime DateCreated { get; set; } public DateTime DateCompleted { get; set; } public string BatchItem { get; set; } public BatchStatus Status { get; set; } } 我的问题-使用NServiceBus,BlockingCollection或ConcurrentQeueue是否比持续轮询数据库并逐一拉出挂起的项目更有效? 我以前没有使用过队列。 一种想法是创建一个任务队列,并在单独的线程上处理所有待处理的任务。 有点类似于处理线程队列的最有效方法,但我想确保自己走的是最有效的路由。 编辑:我在这里有一个大问题是向用户显示进度的最佳方法。 用户提交内容后,将转到新页面,并可以通过批处理标识符查看状态。 为了通知用户
  • Hangfire in ASP.Net Core : simple recurring job does not refresh its dynamic content
    I am trying to implement cron task on a web application in ASP.Net Core 1.1. I decided to choose the Hangfire library. In order to check if my installation and configuration were working fine, I just wrote a very simple recurring job, aimed to print the current DateTime on the console output : RecurringJob.AddOrUpdate(() => Console.WriteLine($"{DateTime.Now}"), Cron.Minutely); The weird thing is that every minute, it prints the same message, corresponding to the date and time of the first time the message was printed, and not updating the DateTime.Now part. If I access the /Hangfire dashboard
  • 您网站上的多个signalR连接/集线器(Multiple signalR connections/hubs on your website)
    问题 如果我有多个页面可以使用多个中心类,那么管理此问题的最佳方法是什么? 例如: 导航到网站上的另一个页面并从本质上“重新打开”与上一页中打开的同一集线器类的连接是否不好? 我是否认为在页面上打开多个集线器连接是正确的,因为即使它们是不同的集线器类,它们也都统一在一个连接中? 回答1 您可以有多个集线器在您的站点上共享一个连接。 SignalR 2.0已更新,可以通过一个信号连接处理多个集线器,而不会降低性能。 官方文档:http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#multiplehubs 所有客户端将使用相同的URL与您的服务建立SignalR连接(“ / signalr”或您的自定义URL(如果您指定了一个)),并且该连接用于该服务定义的所有集线器。 与在单个类中定义所有集线器功能相比,多个集线器没有性能差异。 回答2 要开始使用集线器,请阅读集线器和集线器的客户端的WIKI条目。 根据多个页面的上下文,有几件事情。 启动集线器时,它将为您的客户提供一个ID,该ID在多个页面上都与该集线器相同(可以通过示例进行确认)。 重新打开与同一集线器的连接也不错。 您可能在所有页面上都运行hub.start客户端方法,但是,如果一个客户端打开多个窗口或从一个页面转到另一个页面
  • 如何在asp.net下异步运行持久进程?(How to run long-lasting process asynchronously under asp.net?)
    问题 .net 4.5,asp.net mvc:从ASP.NET应用程序运行持久过程(1-2分钟)的最佳方法是什么,它应该在单线程环境中运行,我的意思是该过程针对一次只有一个用户,其他所有用户的执行都必须等到当前执行完成后才能执行? 场景如下:用户单击运行某种持久计算的按钮,http响应立即返回给用户,然后用户必须手动请求具有单独请求的计算状态。 Asp.net http会话中止不应导致进程终止,而应继续进行。 该过程可能在相同或单独的服务器上运行。 回答1 I would suggest using a product such as NServiceBus to offload the processing and run it in single threaded mode. The advantage to this is that all requests will be processed in order and the processing can be offloaded from the web server as you don't really want long running processes to happen on a web server. 回答2 我将向您展示如何使用http://hangfire.io执行此任务-一种在ASP
  • 从控制器的动作中调用集线器方法(Call a hub method from a controller's action)
    问题 如何从控制器的动作中调用集线器方法? 正确的做法是什么? 有人在帖子中使用了它: DefaultHubManager hd = new DefaultHubManager(GlobalHost.DependencyResolver); var hub = hd.ResolveHub("AdminHub") as AdminHub; hub.SendMessage("woohoo"); 但是对我来说,这是在抛出: 不支持使用不是由HubPipeline创建的Hub实例。 我还阅读了您可以创建中心上下文的信息,但我不想赋予该操作以责任,即该操作执行的操作如下: hubContext.Client(...).someJsMethod(..) 回答1 正确的方法是实际创建中心环境。 取决于您的方式和位置,这是两种方法: 在集线器中创建一个静态方法(不必在集线器中,实际上可以在任何地方),然后可以通过AdminHub.SendMessage("wooo")调用它public static void SendMessage(string msg) { var hubContext = GlobalHost.ConnectionManager.GetHubContext<AdminHub>(); hubContext.Clients.All.foo(msg); } 一起避免使用静态方法
  • SignalR应用程序无法在IIS下运行(SignalR applications do not work under IIS)
    问题 我试图在Visual Studio 2012中构建SignalR应用程序。我的问题是,它在Visual Studio调试下(在Windows 7上使用Visual Studio 2012)运行良好,但是当我尝试在Windows Server 2012上的IIS 8上部署该应用程序时,该应用程序只显示了index.html页面。 我决定尝试缩小问题范围,无论是在我的代码中还是在SignalR中。 我编译了http://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr中显示的SignalR教程。 在Visual Studio上这可以正常工作,但是除了在IIS下显示静态页面外,它不会做任何其他事情。 我尝试了这里列出的一些方法:Signalr / Hub在IIS 7中未加载,但在Visual Studio中正常运行,但似乎都无法正常工作。 回答1 假设您实际上想要查看index.html (即您的问题不是“显示的是空的index.html而不是我的聊天页面chat.html”),那么听起来就像当您在聊天页面中输入内容时,并不是在连接到该聊天的其他浏览器窗口中显示为聊天。 我会尝试一些基本测试。 我将假设: 在服务器上安装并配置了ASP.NET(如果没有,请参见脚注)
  • SignalR可以与asp.net WebForms一起使用吗?(Can SignalR be used with asp.net WebForms?)
    问题 我想在我的项目中使用SignalR进行实时更新。 我的项目是在WebForms开发的。 我搜索了3,4天,但发现的只是MVC示例。 有人可以提出解决方案吗? 回答1 您可以将SignalR与Webforms一起使用。 请参阅下面的教程示例 创建一个针对.NET Framework 4.5或更高版本的新ASP.NET WebForms项目更改主页以包含以下内容<asp:content runat="server" id="BodyContent" contentplaceholderid="MainContent"> <h3>Log Items</h3> <asp:listview id="logListView" runat="server" itemplaceholderid="itemPlaceHolder" clientidmode="Static" enableviewstate="false"> <layouttemplate> <ul id="logUl"> <li runat="server" id="itemPlaceHolder"></li> </ul> </layouttemplate> <itemtemplate> <li><span class="logItem"><%#Container.DataItem.ToString() %></span><
  • Call SignalR Core Hub method from Controller
    How can I call SignalR Core Hub method from Controller? I am using ASP.NET Core 2.0 with Microsoft.AspNetCore.SignalR (1.0.0-alpha2-final). I have windows service which communicate with Excel, SolidEdge ... When operation is complete it post request to my controller in ASP.NET Core application. Now I need to inform all clients connected to server with SignalR that external program completed some task. I can not change the way window service works. (Can not connect to SignalR from window service). I found plenty solution for old SignalR (GlobalHost.ConnectionManager.GetHubContext), but much has