I am using Microsoft.AspNetCore.SignalR
(latest release) and would like to get the hub context from within another object that's not a Controller
. In the "full" SignalR, I could use GlobalHost.ConnectionManager.GetHubContext<MyCoolHub>();
I have seen a lot of examples of just adding Microsoft.AspNetCore.SignalR.IHubContext<MyCoolHub>
as a parameter to the Ctor of a Controller
, but no examples (that work) for otherwise.
ETA:
So, this is what I have working. Is this hacky?
public class MyHub : Hub
public static IHubContext<MyHub> GlobalContext { get; private set; }
public MyHub(IHubContext<MyHub> ctx){
GlobalContext = ctx;
}
}
Then I can call it as so:
await MyHub.GlobalContext.Clients.All.InvokeAsync(...)
Just set IHubContext<MyHub> hubContext
on calling-side constructor.
I would recommend using .net core default DI container mechanism, not creating static property.
Please refer to How do I get a reference to a Hub?
public class MyHub : Hub
{
}
public class CallingSideClass
{
private readonly IHubContext<MyHub> _hubContext;
public CallingSideClass(IHubContext<MyHub> hubContext)
{
_hubContext = hubContext;
}
public async Task FooMethod(...)
{
await _hubContext.Clients.All.InvokeAsync(...);
}
}
public class Startup
{...
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddScoped<CallingSideClass>();
}
...
}
So after looking over this example from the accepted answer, I didn't quite get where he was going, so I tried a few things and I think I got what he was saying. So, for folks who have this issue in the future, I want to change that example to a fully working example.
So we are going to create a "Shared Methods", like in the example:
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace YouDontNeedToKnow.Core.Main.Hubs
{
internal class HubMethods<THub> where THub : Hub
{
private readonly IHubContext<THub> _hubContext;
public HubMethods(IHubContext<THub> hubContext)
{
_hubContext = hubContext;
}
public Task InvokeOnGroupAsync(string groupName, string method, params object[] args) =>
_hubContext.Clients.Group(groupName).InvokeAsync(method, args);
public Task InvokeOnAllAsync(string method, params object[] args) =>
_hubContext.Clients.All.InvokeAsync(method, args);
public Task AddConnectionIdToGroupAsync(string connectionId, string groupName) =>
_hubContext.Groups.AddAsync(connectionId, groupName);
// ...
}
}
Then, in your Hub
object, you add a constructor, like so:
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
namespace YouDontNeedToKnow.Core.Main.Hubs
{
internal class MyHub : Hub
{
public static string HubName => "myHub";
private readonly HubMethods<MyHub> _hubMethods;
public PlayerServicesHub(HubMethods<MyHub> hubMethods)
{
_hubMethods = hubMethods;
}
public override Task OnConnectedAsync()
{
return base.OnConnectedAsync();
}
}
}
In your Startup.cs
, you inject the shared class like so:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<HubMethods<MyHub>>();
services.AddSignalR();
services.AddMvc();
}
This still works as it normally would:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider sp)
{
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseSignalR(routes =>
{
routes.MapHub<MyHub>(MyHub.HubName);
});
app.UseMvc();
// This is just an example line of how you can get the hub with context:
var myHub = sp.GetService<HubMethods<MyHub>>();
}