SignalR将数据传递给包含类

本文关键字:包含类 数据 SignalR | 更新日期: 2023-09-27 18:16:36

我有一个服务,并希望通过SignalR(在OWIN上)添加与之交互的能力。有很多关于如何向客户机发送消息的示例,但是如何从客户机接收消息并将其转发到父服务呢?

public class MyService
{
...
    public void LaunchSR()
    {
        _srWebApp = WebApplication.Start<SrStartup>("http://localhost:" + _signalRPortNumber.ToString());
        _srContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
    }
    public class SrStartup
    {
        public void Configuration(Owin.IAppBuilder app)
        {
            app.MapConnection("/raw", typeof(PersistentConnection), new ConnectionConfiguration { EnableCrossDomain = true });
            app.MapHubs();
        }
    }
    public class MyHub : Hub
    {
        public void SendToServer(string data)
        {
            //!! Don't have a reference to MyService instance,
            //!! so LocalCommand is out of scope
            LocalCommand(data, null);
        }
    }
    public void LocalCommand(data)
    {
        // Do Stuff in the main service context, accessing private objects etc.
    }
}

SendToServer()中的代码有编译时错误:

"不能通过嵌套类型MyHub访问外部类型MyService的非静态成员"。

我知道为什么会这样,但不知道如何正确地做到这一点。

SignalR将数据传递给包含类

Hub实例是暂时的,因此您需要创建某种类型的反转。一种方法是在单例类中创建类似通信代理的东西,两者通过该代理进行通信。

public class HubToServiceProxy {
  public static readonly HubToServiceProxy Instance = new HubToServiceProxy();
  private HubToServiceProxy() {}
  private MyService m_svc;
  // call this when the service starts up, from the service
  public void RegisterService (MyService svc) {
    // Be very careful of multithreading here, 
    //   the Proxy should probably lock m_svc on 
    //   every read/write to ensure you don't have problems.
    m_svc = svc;
  }
  // call this from the hub instance to send a message to the service
  public void SendCommandToService(string data) {
    m_svc.LocalCommand(data, null);
  }  
}

,然后做:

public class MyService
{
  ...
  public MyService() {
    HubToServiceProxy.Instance.RegisterService(this);
  }
  ...
}
public class MyHub : Hub
{
  public void SendToServer(string data)
  {
    HubToServiceProxy.Instance.SendCommandToService(data);
  }
}

注意事项:

  • RegisterService (MyService svc)可以完全删除,用一个监听事件来代替:也就是说,你可以通过事件来实现观察者模式(或者像。net那样):而不是在通信代理中记录服务引用。
    • 服务将监听通信代理上的事件(在实例化时,它将订阅通信代理上的事件),
    • SendCommandToService方法将引发CommandFromClient事件,而不是调用服务。
  • 如果你想坚持将代理和服务耦合:
    • RegisterService (MyService svc)应该是RegisterService (ILocalCommand svc),其中ILocalCommand是定义LocalCommand方法签名的接口,由MyService实现
    • 可以使用静态类,而不是单例(你的调用)
    • 如果你有多个服务实例,你可以保留它们的列表,并将数据发送给所有它们,或者某种关键字字典,它允许你过滤发送数据的位置。
  • 您可以实现工厂,以便在Hub实例化时调用工厂来获取通信代理。这将有助于执行基于DI的测试。
  • 您可能不会通过代理将服务直接公开给Hub,因为这会破坏OO设计的规则,比如封装、内聚和松耦合。

有很多种方法来切割它,你需要根据你的具体情况选择最好的一个。要记住的主要事情是集线器是瞬时的,因此它们必须调用某种非瞬时的服务器端端点,该端点将:将消息传递给服务;或者返回一个对象实例,集线器可以将消息传递给该对象实例以进行分发。显然,这个初始调用不能是另一个类的实例,因此静态或单例类是您唯一的选择。

反过来,这就是GlobalHost.ConnectionManager.GetHubContext<MyHub>()方法允许服务器类的实例与客户端通信的方法。它为您自己的实例提供了一个非瞬态端点,以便在它们想要与客户端通信时调用。