SignalR:对我的集线器进行子分类会打破外部呼叫

时间:2018-02-11 21:57:34

标签: asp.net signalr

我从一个功能正常的SignalR Web应用程序开始,该应用程序具有从SignalR Hub派生的ActivityHub类来管理客户端连接和活动。与stock ticker tutorial类似,还有一个单独的ActivityTimer类,它使用System.Threading.Timer通过它在构造函数中获取的集线器上下文定期向所有客户端广播,如下所示:

activityHubContext = GlobalHost.ConnectionManager.GetHubContext<ActivityHub>();

现在我想将ActivityHub变成一个基类,其中包含用于不同类型活动的子类,在ActivityHub中覆盖一些用于特定于活动的行为的方法,以及使用特定于活动的客户端,每个客户端都引用相应的活动子class(例如,var activityHub = $ .connection.coreActivityHub)。

子分类适用于中心服务器代码和客户端,并且ActivityTimer按预期触发计时器事件,但ActivityTimer调用不再到达客户端。如果我获得特定活动子类的中心上下文,它将再次起作用,但仅适用于该子类:

activityHubContext = GlobalHost.ConnectionManager.GetHubContext<CoreActivityHub>();

有没有办法让一个通用的ActivityTimer能够与ActivityHub的所有子类一起使用? ActivityTimer可以调用基本ActivityHub类中的某个方法,而不是尝试直接访问所有客户端(基类似乎没有问题调用Clients.All.doSomething())?

如果它简化了事情(或者使得其他方面具有挑战性的解决方案),应用程序将一次只运行一种类型的活动 - 所有客户端将同时处于同一活动中。

1 个答案:

答案 0 :(得分:0)

在处理同一项目中的其他问题时,我遇到this,其中指向this,我在其中找到了this(如果主题,我们都值得快速阅读感兴趣的你)。它们提供了一种方法来完成我想要做的事情:在基类中有一个方法可以从“外部”调用以到达任何/所有子类的客户端。 (他们还帮助我更清楚地思考了集线器上下文,以及为什么我认为我的原始ActivityTimer无法使用子类 - 请参阅本答案末尾的注释以获得进一步的解释。)

我的问题的解决方案是在基类中创建一个方法来调用客户端,然后从ActivityTimer调用这个新方法以间接地到达客户端。这种方法不依赖于ActivityTimer中的集线器上下文,它使我们不必担心子类,因为它明确地调用了基类:

  1. 在基类中创建一个静态字段以保存基类的集线器上下文:

    private static IHubContext thisHubContext;
    
  2. 在每个子类的构造函数中使用该类作为传递给GetHubContext()的类型设置此中心上下文:

    thisHubContext = 
        GlobalHost.ConnectionManager.GetHubContext<CoreActivityHub>();
    
  3. 在基类中创建一个静态方法,该方法调用所需的客户端方法;请注意,您可以使用除Clients.All之外的其他选项来访问客户端的子集(例如,arg可能指定要到达的SignalR组):

    public static void DoSomething(string someArg)
    {
        thisHubContext.Clients.All.doSomething(someArg);
    }
    
  4. 从集线器“外部”的任何服务器代码调用此基类方法。在我的例子中,我从ActivityTimer中的timer事件处理程序调用它:

    ActivityHub.DoSomething("foo");
    
  5. 消息将传递给静态方法中指定的客户端。

    注意:此解决方案仅适用于原始帖子末尾提到的特定情况,其中一次只使用一个子类,因为每个子类将基类静态集线器上下文设置为它自己的背景。我还没有试图找到解决这个限制的方法。

    注意:我认为通过存储的集线器上下文使“外部集线器”服务器代码与子类一起工作是不可能的。在我原来运行的应用程序中(在我尝试创建ActivityHub的子类之前),ActivityTimer通过它实例化的集线器上下文与客户端进行对话:

    public ActivityTimer()
    {
        activityHubContext = GlobalHost.ConnectionManager.GetHubContext<ActivityHub>();
        activityTimer = new Timer(DoSomething, null, TimerInterval, TimerInterval);
    }
    
    public void DoSomething(object state)
    {
        activityHubContext.Clients.All.doSomething("foo");
    }
    

    因为集线器上下文是通过显式引用特定类(在本例中为ActivityHub)获得的,所以它不适用于子类。如果相反(正如我在原始帖子中提到的那样)我获得了特定子类的集线器上下文,计时器现在将用于那个子类的实例,而不是其他子类;同样,问题是为特定的子类获得了集线器上下文。

    我认为没有办法解决这个问题,因此我认为唯一的解决方案是上面概述的解决方案,其中基类方法使用子类构造函数设置的集线器上下文,外部代码调用基础通过该子类上下文获取客户端的类方法。

    但是,我仍然处于SignalR学习曲线(以及其他方面),所以将会感谢任何更正或澄清!