1

我有一项服务,并希望增加通过 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 的非静态成员”。

我理解为什么会发生这种情况,但不知道如何正确执行此操作。

4

1 回答 1

2

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>()方法允许服务器类的实例与客户端通信的方法。它为您自己的实例提供了一个非瞬态端点,以便在它们想要与客户端通信时调用。

于 2013-06-05T15:26:45.557 回答