Reputation: 781
I want to inject my strongly typed hub in a service, but I don't like certain thing in the example shown by Microsoft - https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-2.2 (Inject a strongly-typed HubContext)
public class ChatController : Controller
{
public IHubContext<ChatHub, IChatClient> _strongChatHubContext { get; }
public ChatController(IHubContext<ChatHub, IChatClient> chatHubContext)
{
_strongChatHubContext = chatHubContext;
}
public async Task SendMessage(string message)
{
await _strongChatHubContext.Clients.All.ReceiveMessage(message);
}
}
In this example ChatHub
is coupled to ChatController
.
So I want to inject the hub itself defined with generic interface parameter and no concrete implementation of it will be defined in my service. This is sample code
public interface IReportProcessingClient
{
Task SendReportInfo(ReportProgressModel report);
}
public class ReportProcessingHub : Hub<IReportProcessingClient>
{
public async Task SendMessage(ReportProgressModel report)
{
await Clients.All.SendReportInfo(report);
}
}
public class ReportInfoHostedService : IHostedService, IDisposable
{
private readonly Hub<IReportProcessingClient> _hub;
private readonly IReportGenerationProgressService _reportService;
public ReportInfoHostedService(Hub<IReportProcessingClient> hub, IReportGenerationProgressService reportService)
{
_hub = hub;
_reportService = reportService;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_reportService.SubscribeForChange(async x =>
{
await _hub.Clients.All.SendReportInfo(x);
});
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public void Dispose()
{
}
}
This approach obviously will need additional registration of the hub in Startup.cs as it is not called by the context api provided by Microsoft.
services.AddSingleton<Hub<IReportProcessingClient>, ReportProcessingHub>();
app.UseSignalR(route => {
route.MapHub<ReportProcessingHub>("/reportProcessingHub");
});
All done and working until the hub is trying to send messages to Clients. Then I get the exception
_hub.Clients.All threw an exception of System.NullReferenceException: 'Object reference not set to an instance of an object.'
So to summerize:
1. Is this the right way to inject strongly typed hubs and what am I doing wrong(e.g. wrong registration of the hub in services, wrong usage of app.UseSingleR
)?
2. If not, what is the correct way?
NOTE:
I know there is a lot easier way injecting IHubContext<Hub<IReportProcessingClient>>
, but this is not a solution for me, because I have to call the hub method name passed as string
parameter.
Upvotes: 5
Views: 2629
Reputation: 25350
I want to ... and no concrete implementation of it will be defined in my service
If you don't want to expose a concrete hub implementation, you should at least expose a base class or interface. However, since a hub should inherit from the Hub
class, we can't use an interface here. So let's create a public base hub ReportProcessingHubBase
as well as an internal concrete ReportProcessingHub
:
public abstract class ReportProcessingHubBase : Hub<IReportProcessingClient>
{
public abstract Task SendMessage(ReportProgressModel report);
}
// the concrete hub will NOT be exposed
internal class ReportProcessingHub : ReportProcessingHubBase
{
public override async Task SendMessage(ReportProgressModel report)
{
await Clients.All.SendReportInfo(report);
}
}
Make sure you've registered the two related service:
services.AddScoped<ReportProcessingHubBase, ReportProcessingHub>();
services.AddHostedService<ReportInfoHostedService>();
endpoints.MapHub<ReportProcessingHubBase>("/report");
Finally, you can get the base hub by injecting IHubContext<ReportProcessingHubBase,IReportProcessingClient>
:
public class ReportInfoHostedService : IHostedService, IDisposable
{
private readonly IHubContext<ReportProcessingHubBase,IReportProcessingClient> _hub;
public ReportInfoHostedService(IHubContext<ReportProcessingHubBase,IReportProcessingClient> hub)
{
_hub = hub;
}
...
}
Now, you can invoking the hub method in a strongly-typed way.
Upvotes: 7