I have a web app that has a hosted service derived from BackgroundService that supposed to periodically poll Azure for some data and when found send the data over SignalR to a client.
I have 2 clients: a web client (Razor Pages + javascript) that gets the message and displays the data.
The 2nd client I am working on is a desktop client.
On the web side my polling function in the monitoring service makes a signalR call delivering data:
var connectionId = _connectionMappingService.GetConnectionId(userName);await
IndexStats? currentStats = await _indexStatsService.GetIndexStatsAsync(searchIndexName);
_hubContext.Clients.Client(connectionId).SendAsync("IndexStats", currentStats);
I verified that the call is made, and the data is correct. 'currentStats' is a complex object. I simplified code here for brevity...
The _hubContext here is injected by DependencyInjection in the service constructor:
public IndexStatsPolling(ILogger<IndexStatsPolling> logger,
IHubContext<NotificationHub> hubContext,
IConnectionMappingService connectionMappingService,
IAzureSettings azureSettings,
IIndexStatsService indexStatsService)
{
_logger = logger;
_hubContext = hubContext;
_connectionMappingService = connectionMappingService;
_azureSettings = azureSettings;
_indexStatsService = indexStatsService;
}
On the desktop side I have SignalRClient class:
public class SignalRClient : ISignalRCleint
{
private HubConnection hubConnection;
public event Action<IndexStats> OnIndexStatsReceived;
public SignalRClient(string url)
{
Init(url);
}
private void Init(string url)
{
using (Log.VerboseCall())
{
string err = "Failed to connect to hub or set event handler";
try
{
hubConnection = new HubConnectionBuilder().WithUrl(url).Build();
if (String.IsNullOrWhiteSpace(url))
{
throw new Exception(err);
}
Log.VerboseFormat($"Created hubConnection for URL: {url}");
hubConnection.KeepAliveInterval = new TimeSpan(0,0,5);
hubConnection.HandshakeTimeout = new TimeSpan(0,2,0);
// "IndexStats" is just an endpoint alias
hubConnection.On<IndexStats>("IndexStats", (indexStats) =>
{
// Handle the received IndexStats object
// For example, updating the UI or processing the data
OnIndexStatsReceived?.Invoke(indexStats);
});
}
catch (Exception ex)
{
Log.Verbose(ex);
throw new Exception(err);
}
}
}
public async Task ConnectAsync()
{
using(Log.VerboseCall())
{
try
{
await hubConnection.StartAsync();
Log.VerboseFormat($"Connection state: {hubConnection.State}");
}
catch(Exception ex)
{
Log.Verbose(ex);
}
}
}
public async Task DisconnectAsync()
{
if (hubConnection != null && hubConnection.State == HubConnectionState.Connected)
{
await hubConnection.StopAsync();
}
}
public event Action<string> OnMessageReceived;
public async Task SendMessageAsync(string message)
{
// Implementation to send message...
}
}
On the desktop SignalR is registered in Program.cs:
// Add SignalRCleint
services.AddScoped<SignalRClient, SignalRClient>(provider =>
{
var azConfig = provider.GetRequiredService<IAzConfig>();
var signalRHubUrl = azConfig.AzureADDesktop.SignalRHubUrl; // Now this should have the correct value
return new SignalRClient(signalRHubUrl);
});
Then in the MainForm.cs:
private async void MainForm_Load(object sender, EventArgs e)
{
_signalRClient.OnIndexStatsReceived += SignalRClient_OnIndexStatsReceived;
await _signalRClient.ConnectAsync();
}
private void SignalRClient_OnIndexStatsReceived(IndexStats indexStats)
{
using (Log.VerboseCall())
{
string indexName = indexStats.IndexName;
UpdateGlobalStats(UpdateType.IngestData, indexName, indexStats);
}
}
The bottom line is the SignalRClient_OnIndexStatsReceived is never called...
On the client side I use the following NuGet packages: Microsoft.AspNetCore.SignalR.Client 8.0.0 Microsoft.AspNetCore.SignalR.Client.Core 8.0.0
and on the web app side: Microsoft.AspNetCore.SignalR.Core 1.0.15 (the latest stable version)
If anybody has a clue about this, I'd be glad for any solution, hint, pointer - I spent a lot of time trying to resolve it but in vain...
Eventually it was not compatibility of libraries issue. My desktop client was simply not authenticated with signalR server and therefore internally the connection in the server was immediately disconnecting despite the client was seeing initial Connected state.