How can I get a SignalR HubContext using a base hub type to access the same derived hub instance?
See original GitHub issueI apologise for the somewhat confusing nature of the title; I was unsure how to properly word it. If there’s a better way of classifying what I’m trying to do, I will try to reword the title (and this post) for greater clarity.
The Hub
I have a SignalR hub, BaseHub, that inherits from Hub<T>. BaseHub is then inherited by TestHub. This hub is mapped via `routes.MapHub<TestHub>(“/hub”).
The BaseHub class is stored in a library, along with some controllers. The TestHub class is stored in the ASP.NET Core API project itself, which contains more controllers.
The Problem
When injecting the HubContext into one of my controllers in the API project I specify IHubContext<TestHub, ITestClient> hubContext as the constructor parameter, which works as expected.
When injecting the HubContext into one of my controllers in the library project I specify IHubContext<BaseHub, ITestClient> hubContext as the constructor parameter, which does not work as expected. There are no exceptions thrown, and a HubContext instance is supplied, but SignalR notifications are not sent using this instance.
When looking at the non-public members of this HubContext instance in the watch window, I can see that HubContext.Clients._lifetimeManager._connections.Count is 0 even though I know there are definitely connections to the hub (for the one that works, this figure is above zero when I inspect the HubContext instance using the watch window).
My best guess is it’s expecting there to be a different hub instance available of type BaseHub.
The Question
What can I do to ensure that injections of IHubContext<TestHub, ITestClient> and IHubContext<BaseHub, ITestClient> reference the same hub instance?
For example, with my DbContext classes, I am able to do services.AddScoped<BaseDbContext>(f => f.GetRequiredService<DerivedDbContext>());, however that doesn’t seem possible here. For starters, the MapHub() part happens in Configure() rather than ConfigureServices(). Even if I ignore that, and attempt to add the service regardless, it looks like IHubContext doesn’t support covariance (I think that’s the correct term?), so the following throws an InvalidCastException:
services.AddScoped<IHubContext<BaseHub<ITestClient>>>(f => (IHubContext<BaseHub<ITestClient>>)f.GetRequiredService<IHubContext<TestHub>>());
Is what I am attempting possible? Or is my only alternative to either move those shared controllers out of the library project, or to create derived controllers that inject the correct HubContext?
Issue Analytics
- State:
- Created 3 years ago
- Reactions:2
- Comments:13 (3 by maintainers)
Top Related StackOverflow Question
Hey, @Metritutus when you use MapHub<THub> it will add a connection middleware, it uses ActivatorUtilities.GetServiceOrCreateInstance<T>() for create or get your hub one time (when you add);
With this you can inject in D.I a singleton like that: <BaseHub, TestHub> and instead use
MapHub<TestHub>you can useMapHub<BaseHub>and it will get your hub from the dependency container.I don’t know why when you requested an IHubContext<BaseHub, ITestClient> it doesn’t throw an exception, probably because IHubContext is injected in D.I in this way:
By default this wont work, but you might be able to set it up to work this way by doing something like:
services.AddSingleton<IHubContext<BaseHub>, IHubContext<TestHub>>();