2013-06-04 61 views
1

我有一个服务并希望添加通过SignalR(在OWIN上)与它交互的功能。有很多关于如何向客户端发送消息的示例,但是如何从客户端接收消息并将消息转发给父服务?SignalR将数据传递给包含类

例如

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()具有编译时错误:

"cannot access a non-static member of outer type MyService via nested type MyHub".

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

回答

2

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

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

反过来,这就是GlobalHost.ConnectionManager.GetHubContext<MyHub>()方法可以允许server classes to communicate with clients的实例。它为您自己的实例提供了一个非暂时性端点,以便在他们想与客户进行通信时进行调用。

+0

谢谢!准确的代码,易于集成,简明而全面的讨论注意事项和选项。 – swordfishBob

+0

小修改:其中一个总结句子含糊不清,最初遗漏了一个关于封装和耦合的重要问题。 –

+0

看起来最清洁的方法是使用事件。 – swordfishBob