SignalR client-server connection issues
I've been trying to figure out the problem all day and can't solve it. Here is my SignalR hub ( BlazorServerAppHub.cs )
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using BlazorServerApp.Data;
namespace BlazorServerApp
{
public class BlazorServerAppHub: Hub
{
public const string HubUrl = "/chat";
public async Task Broadcast(WeatherForecast[] forecasts)
{
await Clients.All.SendAsync("ReceiveMessage", forecasts);
}
public override Task OnConnectedAsync()
{
return base.OnConnectedAsync();
}
}
}
This is the business logic page ( WeatherForecastService.cs )
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using BlazorServerApp.Data;
using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Timers;
namespace BlazorServerApp.Data
{
public class WeatherForecastService
{
private readonly IHubContext<BlazorServerAppHub> _hubContext;
public WeatherForecastService(IHubContext<BlazorServerAppHub> hubContext)
{
_hubContext = hubContext;
}
protected async Task Broadcast()
{
var forecasts = await GetForecastAsync();
await _hubContext.Clients.All.SendAsync("Broadcast", forecasts);
}
public Task<WeatherForecast[]> GetForecastAsync()
{
var rng = new Random();
}
}
public class WeatherForecast
{
public int A { get; set; }
}
}
Finally, here is my razor page ( FetchData.razor )
@page "/fetchdata"
@using BlazorServerApp.Data;
@inject NavigationManager navigationManager
@using Microsoft.AspNetCore.SignalR.Client;
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>A</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.A</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[] forecasts;
private HubConnection _hubConnection;
protected override async Task OnInitializedAsync()
{
string baseUri = navigationManager.BaseUri;
string hubUrl = baseUri.TrimEnd('/') + BlazorServerAppHub.HubUrl;
_hubConnection = new HubConnectionBuilder().WithUrl(hubUrl).Build();
_hubConnection.On< WeatherForecast[]>("Broadcast", forcast => { forecasts = forcast; StateHasChanged(); });
await _hubConnection.StartAsync();
}
}
Basically, I'm trying to get data from the server and push it to the client periodically. The problem is that when I run the app forecasts
in FetchData.razor the page is empty, so the page says "Loading". why is that? My guess is that I am missing something in the SignalR communication.
In order for the server to periodically push data to the client, you need to run some kind of background service. There are multiple ways to do this, for example:
However, since you said you only want to send data once for testing, you can hook into sending data to clients Hub.OnConnectedAsync()
when they initially connect . First, I recommend creating a separate interface/repository for Task<WeatherForecast[]> GetForecastAsync()
:
public interface IWeatherForecastRepository
{
Task<WeatherForecast[]> GetForecastAsync();
}
public class WeatherForecastRepository
{
private static readonly string[] Tickers = new[]
{
"10", "20", "30", "44", "77"
};
private static readonly int[] Ones = new[]
{
1000, 15000, 7000, 500, 2200
};
public Task<WeatherForecast[]> GetForecastAsync()
{
var rng = new Random();
return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
A = index,
B = Tickers[index - 1],
C = NextFloat(rng),
D = Ones[index - 1],
E = rng.Next(0, 10000),
}).ToArray());
}
static float NextFloat(Random random)
{
double decimalPart = random.NextDouble();
double intPart = random.Next(0, 1000);
return (float)Math.Round(intPart + decimalPart, 3); ;
}
}
Then, when you register that repository with DI, you can inject it into your own library BlazorServerAppHub
and use OnConnectedAsync()
:
public class BlazorServerAppHub : Hub
{
public const string HubUrl = "/chat";
private readonly IWeatherForecastRepository _weatherForecastRepository;
public BlazorServerAppHub(IWeatherForecastRepository weatherForecastRepository)
{
_weatherForecastRepository = weatherForecastRepository;
}
public async Task Broadcast(WeatherForecast[] forecasts)
{
await Clients.All.SendAsync("ReceiveMessage", forecasts);
}
public override async Task OnConnectedAsync()
{
await base.OnConnectedAsync();
var forecasts = await _weatherForecastRepository.GetForecastAsync();
// Clients.Caller will only send the data to the client that just connected
await Clients.Caller.SendAsync("Broadcast", forecasts);
}
}