Reputation:
After finding this update around communicating with groups, it appears that a group is keyed off the hub name. If this is correct (please let me know if I'm mistaken) is there any way for a hub to access another hub's group or better yet to have some kind of global group?
My problem is that I have one hub that adds the client to a group:
public class GameService : Hub, IGameService
...
public void CreateNewGame(CreateGameDto game)
{
game.Creator = UserRepo.GetUser(Context.ConnectionId);
var newGame = GameRepo.CreateNewGame(game);
Groups.Add(Context.ConnectionId, newGame.GameId.ToString(CultureInfo.InvariantCulture));
Clients.Caller.JoinedGame(newGame);
}
and another hub entirely needs to broadcast to that group:
public class MessagingService : Hub , IMessageService
...
public void AddMessage(MessageDto message)
{
message.User = UserRepo.GetUser(Context.ConnectionId);
MessageRepo.AddMessage(message);
Clients.Group(message.User.GameId.ToString(CultureInfo.InvariantCulture))
.ReceivedMessage(message);
}
As of right now, the client is never receiving this broadcast.
Edited to add the client code. Here is the client side signalr setup, I've created a framework that allows me to use the service interface with a Castle Dynamic Proxy so that I'm not calling services and methods by string name.
public abstract class BaseClientProxy<TServer,TClient>
{
public TServer ServiceProxy;
protected BaseClientProxy(HubConnection conn)
{
ServiceProxy = CreateProxy(conn);
}
private ProxyGenerator _generator;
protected IHubProxy Proxy;
protected TClient Receiver;
protected TServer CreateProxy(HubConnection conn)
{
Proxy = conn.CreateHubProxy<TServer>();
_generator = new ProxyGenerator();
return (TServer)_generator.CreateInterfaceProxyWithoutTarget(typeof(TServer), new HubProxyInterceptor(Proxy));
}
public void SetReceiver(TClient receiver)
{
Receiver = (TClient)_generator.CreateInterfaceProxyWithTarget(typeof(TClient), receiver);
RegisterEvents();
}
protected void RegisterEvents()
{
Action<MethodInfo> regAction = RegisterEvent<object>;
var methods =
typeof (TClient).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
foreach (var methodInfo in methods)
{
var locInfo = methodInfo;
if (locInfo.GetParameters().Length > 0)
{
var regMethod = typeof(BaseClientProxy<TServer, TClient>).GetMethodExt(regAction.Method.Name, typeof(MethodInfo));
var genAction = regMethod.MakeGenericMethod(locInfo.GetParameters()[0].ParameterType);
genAction.Invoke(null, new object[] { locInfo });
}
else
{
Proxy.On(locInfo.Name, () =>locInfo.Invoke(Receiver, null));
}
}
}
protected void RegisterEvent<TDto>(MethodInfo method)
{
Proxy.On<TDto>(method.Name, x => method.Invoke(Receiver, new object[] {x}));
}
}
public class HubProxyInterceptor : IInterceptor
{
protected IHubProxy Proxy;
public HubProxyInterceptor(IHubProxy proxy)
{
Proxy = proxy;
}
protected async void Invoke<TDto>(string methodName, TDto dto)
{
await Proxy.Invoke(methodName, dto);
}
protected async void Invoke(string methodName)
{
await Proxy.Invoke(methodName);
}
public void Intercept(IInvocation invocation)
{
if (invocation.Arguments.Length > 0)
{
Invoke(invocation.Method.Name, invocation.Arguments[0]);
}
else
{
Invoke(invocation.Method.Name);
}
}
}
Theses snippets are from a singleton class that establishes the connection and registers the services in a windsor container.
private SignalRManager()
{
var settings = Settings.GetSettings<IClientSettings>(ConfigurationManager.AppSettings);
Connection = new HubConnection(settings.SignalRServerUri);
ioc = new WindsorContainer();
ioc.Register(
Component.For<BaseClientProxy<IUserService, IUserClient>>().Instance(new UserServiceProxy(Connection)),
Component.For<BaseClientProxy<IDrawingService, IDrawingClient>>().Instance(new DrawingServiceProxy(Connection)),
Component.For<BaseClientProxy<IMessageService, IMessageClient>>().Instance(new MessageServiceProxy(Connection)),
Component.For<BaseClientProxy<IScoreBoardService, IScoreBoardClient>>().Instance(new ScoreBoardServiceProxy(Connection)),
Component.For< BaseClientProxy<IGameService,IGameClient>>().Instance(new GameServiceProxy(Connection)));
Connection.Start().Wait();
}
public TClient GetService<TClient,TReceiver>(TReceiver receiver) where TClient : IService
{
var proxy = ioc.Resolve<BaseClientProxy<TClient, TReceiver>>();
proxy.SetReceiver(receiver);
return proxy.ServiceProxy;
}
In this code the HubProxyInterceptor is responsible for making all calls to the server.
And the RegisterEvents method within the BaseClientProxy is responsible for wiring up the calls from the server to the client. The TServer and TClient type parameters are 2 different but similar interfaces, TServer will be implemented by the Hub class on the server side and TClient is implemented by my WPF view model which is what is passed in in the call to SetReceiver.
Upvotes: 5
Views: 1843
Reputation: 15244
You can use GetHubContext in MessagingService.AddMessage to send messages to another Hub's groups.
public void AddMessage(MessageDto message)
{
IHubContext gameContext = GlobalHost.ConnectionManager.GetHubContext<GameService>();
message.User = UserRepo.GetUser(Context.ConnectionId);
MessageRepo.AddMessage(message);
gameContext.Clients.Group(message.User.GameId.ToString(CultureInfo.InvariantCulture))
.ReceivedMessage(message);
}
You can store the gameContext
in a field if you don't want to recreate it on every call to AddMessage.
Upvotes: 4