The Hub instances are transient, so you need to create some kind of inversion. One way is to create something like a communication proxy in a singleton class that the two communicate through.
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);
}
}
and then do:
public class MyService
{
...
public MyService() {
HubToServiceProxy.Instance.RegisterService(this);
}
...
}
public class MyHub : Hub
{
public void SendToServer(string data)
{
HubToServiceProxy.Instance.SendCommandToService(data);
}
}
A few points to note:
RegisterService (MyService svc)
could be removed entirely and replaced with an event to listen to: i.e. you could implement the Observer pattern through events (or as close as .NET does it): instead of recording the service reference in the communication proxy.
- The service would listen for events on the communication proxy (on instantiation it would subscribe to the events on the communication proxy),
- and the
SendCommandToService
method would raise the CommandFromClient
event instead of calling the service.
- If you want to stick with coupling the proxy and service:
RegisterService (MyService svc)
should probably be RegisterService (ILocalCommand svc)
where ILocalCommand
is an interface that defines the LocalCommand
method signature, and is implemented by MyService
- You could use a static class, not a singleton (your call)
- If you have multiple service instances you can keep a list of them and send the data to all of them, or some kind of keyed dictionary which allows you to filter where you send the data.
- You could implement factories instead so that on instantiation of the Hub it calls a factory to get the communication proxy. This would help you perform DI based testing.
- You probably wouldn't expose the service directly to the Hub through the proxy as this starts to break the rules of OO design such as encapsulation, cohesion and loose coupling.
There are a variety of ways to cut this, you need to choose the best one depending on your exact situation. The main thing to remember is that Hubs are transient, so they have to call some kind of non-transient server-side endpoint which will either: pass the message to the service; or return an object instance the hub can pass a message to for distribution. This initial call cannot, obviously, be an instance of another class, so static or singleton classes are your only option.
In reverse, this is what the GlobalHost.ConnectionManager.GetHubContext<MyHub>()
method does to allow instances of your server classes to communicate with clients. It provides a non-transient endpoint for your own instances to call when they want to communicate with clients.