It seems that Microsoft has made changes to SignalR. At least I think. I created a new project and the dependencies are:
It is not:
I want to send notification to the client but not from the hub itself like the sample available at: (Tutorial: Get started with ASP.NET Core SignalR)
According to Microsoft doc (purple note), Hub
class is transient and we should access it from IHubContext
(not the Hub directly).
I read: Microsoft how SignalR works but I can't figure out how to access the Hub
through IHubContext<MyHub>
because I can't find how to get my IHubContext<MyHub>
. Either in the C# Corner (link below), I can't see how to call the background service to get a "IHubContext"?
So according to the doc, I can't instantiate MyHub directly. I have to pass through IHubContext. But I can't figured out how to get a valid IHubContext<MyHub>
from anywhere in the web server in order to push notification to the client?
Note: I can't figured out, either based on these links:
I found 2 ways to get a SignalR HubContext. Both depends on Magic. The Magic of dependency injection where Microsoft uses with questionnable reasons.
First, as the doc says, you can't get the Hub itself: Hubs are transient. We should use HubContext instead. That appears to be silly to me because my intension was to put code related to Hub in the class itself but because I cannot have access to that class in any ways when from a worker thread, it makes no sense anymore.
I found 2 ways to get an instance:
Method 1 - Twisted one, by creating a HostedService and overloading the constructor like in code below. The hubContext will happen as an argument of the constructor by magic - don't ask me why and good luck to find it. ASP.NET is more and more using dependency injection and magic happen everywhere. Programmer need to guess where and why. I think it makes library very hard to understand for peoples not knowing the inner ASP.Net code or all the nits and bits of the library design.
In class program:
builder.Services.AddHostedService<LogHubService>();
LogHubService constructor:
public class LogHubService : BackgroundService
{
IHubContext<LogHub> _hubContext;
public LogHubService(IHubContext<LogHub> hubContext)
{
_hubContext = hubContext;
}
...
Method 2 - Using the "WebApplication.Services". That solution also depends on Magic. Again, it is still depends on dependency injection and magic. I have no clue why Microsoft went for magic where there is no way to discover where to get the HubContext (there is no ref in code). That appears to me as being a totally stupid design, exactly like the magic that happen in Java-Spring (which makes me vomit). Also (just to show the weird design) they call a connection: client where a client can have more than a connection. You should also pass a connectionId to distinguish the proper Client (appears to be stupid to me). The way I was able to get my HubContext was using the WebApplication.Services like so (but don't ask me why):
var hubContext = Program.WebApplication.Services.GetService<IHubContext<ItemHub>>();
Important here: I created static public property into "Program" class to access my WebApplication object. I initialize that property on startup.
public class Program
{
public static WebApplication WebApplication { get; set; }
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//EO:
builder.Services.AddSignalR();
var app = builder.Build();
//EO:
WebApplication = app;
...