SignalR将数据传递给包含的类

时间:2013-06-04 01:53:14

标签: c# signalr

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

e.g。

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的非静态成员”。

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

1 个答案:

答案 0 :(得分:2)

中心instances are transient,因此您需要创建某种反转。一种方法是在单例类中创建类似通信代理的东西,两者通过它进行通信。

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)可以完全删除并替换为要监听的事件:即您可以通过事件(或as close as .NET does it)实现Observer模式:而不是在通信代理中记录服务引用。
    • 该服务将侦听通信代理上的事件(在实例化时,它将订阅通信代理上的事件),
    • 并且SendCommandToService方法会引发CommandFromClient事件而不是调用服务。
  • 如果您想坚持使用代理和服务的耦合:
    • RegisterService (MyService svc)应该是RegisterService (ILocalCommand svc),其中ILocalCommand是定义LocalCommand方法签名的接口,由MyService实现
    • 可以使用静态类,而不是单例(your call
    • 如果您有多个服务实例,您可以保留它们的列表并将数据发送给所有服务实例,或者某种键控字典,它允许您过滤发送数据的位置。
  • 您可以实现工厂,以便在实例化Hub时调用工厂来获取通信代理。这将有助于您执行基于DI的测试。
  • 您可能不会通过代理将服务直接暴露给Hub,因为这会破坏OO设计的规则,例如封装,内聚和loose coupling

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

相反,这是GlobalHost.ConnectionManager.GetHubContext<MyHub>()方法允许server classes to communicate with clients实例的操作。它为您自己的实例提供非瞬态端点,以便在他们想要与客户端通信时进行调用。