Skip to content

Commit d276548

Browse files
committed
Add DashboardStateService for Blazor Server state management
- Create singleton DashboardStateService to share state across circuits - Update DataPollingService to use state service instead of just SignalR - Register state service in Program.cs - This fixes the issue where Blazor Server components can't use client-side SignalR connections - Components subscribe to OnDataChanged event instead of creating HubConnection This solves the dashboard issue: server broadcasts data but clients can't receive it. Blazor Server components run on the server and share state through services, not SignalR hubs.
1 parent e209b73 commit d276548

File tree

3 files changed

+50
-4
lines changed

3 files changed

+50
-4
lines changed

FabrikamDashboard/BackgroundServices/DataPollingService.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ namespace FabrikamDashboard.BackgroundServices;
88

99
/// <summary>
1010
/// Background service that polls FabrikamApi and FabrikamSimulator every few seconds
11-
/// and broadcasts updates to all connected clients via SignalR
11+
/// and updates the dashboard state for all connected clients
1212
/// </summary>
1313
public class DataPollingService : BackgroundService
1414
{
1515
private readonly IServiceProvider _serviceProvider;
1616
private readonly IHubContext<DashboardHub> _hubContext;
17+
private readonly DashboardStateService _stateService;
1718
private readonly ILogger<DataPollingService> _logger;
1819
private readonly TimeSpan _pollingInterval = TimeSpan.FromSeconds(5);
1920
private readonly SemaphoreSlim _refreshTrigger = new(0);
@@ -22,10 +23,12 @@ public class DataPollingService : BackgroundService
2223
public DataPollingService(
2324
IServiceProvider serviceProvider,
2425
IHubContext<DashboardHub> hubContext,
26+
DashboardStateService stateService,
2527
ILogger<DataPollingService> logger)
2628
{
2729
_serviceProvider = serviceProvider;
2830
_hubContext = hubContext;
31+
_stateService = stateService;
2932
_logger = logger;
3033
}
3134

@@ -128,11 +131,14 @@ private async Task PollAndBroadcastAsync(CancellationToken cancellationToken)
128131
// Calculate dashboard metrics
129132
var dashboardData = CalculateDashboardMetrics(orders, tickets, analytics, simulatorStatus);
130133

131-
// Broadcast to all connected clients
134+
// Update state service (triggers component updates)
135+
_stateService.UpdateData(dashboardData);
136+
137+
// Also broadcast via SignalR for any external clients
132138
await _hubContext.Clients.All.SendAsync("DashboardUpdate", dashboardData, cancellationToken);
133139

134-
_logger.LogInformation("📡 Broadcasted dashboard update: {Orders} orders, {Tickets} tickets, ${Revenue:N0} revenue to {ClientCount} clients",
135-
dashboardData.TotalOrders, dashboardData.OpenTickets, dashboardData.TotalRevenue, _hubContext.Clients.All);
140+
_logger.LogInformation("📡 Updated dashboard state: {Orders} orders, {Tickets} tickets, ${Revenue:N0} revenue",
141+
dashboardData.TotalOrders, dashboardData.OpenTickets, dashboardData.TotalRevenue);
136142
}
137143

138144
private DashboardDataDto CalculateDashboardMetrics(

FabrikamDashboard/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
builder.Services.AddRazorComponents()
1010
.AddInteractiveServerComponents();
1111

12+
// Add dashboard state management
13+
builder.Services.AddSingleton<DashboardStateService>();
14+
1215
// Add SignalR for real-time updates
1316
builder.Services.AddSignalR(options =>
1417
{
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using FabrikamDashboard.Models;
2+
3+
namespace FabrikamDashboard.Services;
4+
5+
/// <summary>
6+
/// Singleton service that holds the current dashboard state
7+
/// and notifies subscribers when data changes
8+
/// </summary>
9+
public class DashboardStateService
10+
{
11+
private DashboardDataDto? _currentData;
12+
private readonly object _lock = new();
13+
14+
public event Action? OnDataChanged;
15+
16+
public DashboardDataDto? CurrentData
17+
{
18+
get
19+
{
20+
lock (_lock)
21+
{
22+
return _currentData;
23+
}
24+
}
25+
}
26+
27+
public void UpdateData(DashboardDataDto newData)
28+
{
29+
lock (_lock)
30+
{
31+
_currentData = newData;
32+
}
33+
34+
// Notify all subscribers on the UI thread
35+
OnDataChanged?.Invoke();
36+
}
37+
}

0 commit comments

Comments
 (0)