How to Persist SignalR Broadcasted Data on Blazor Server Page After Navigation?

Cenk 1,021 Reputation points
2024-11-19T15:28:32.5+00:00

Hello,

I am broadcasting measurement data from an API and persisting it in a database.

[ApiController]
[Route("api/[controller]")]
public class MeasurementsController : ControllerBase
{
    private readonly IHubContext<WeighingHub> _hubContext;
    private readonly ILogger<MeasurementsController> _logger;
    private readonly IMeasurementService _measurementService;

    public MeasurementsController(IHubContext<WeighingHub> hubContext, ILogger<MeasurementsController> logger, IMeasurementService measurementService)
    {
        _hubContext = hubContext;
        _logger = logger;
        _measurementService = measurementService;
    }

    [HttpPost]
    public async Task<IActionResult> Post(Measurement measurement)
    {
        _logger.LogInformation("Received measurement: {@Measurement}", measurement);
        await _hubContext.Clients.All.SendAsync("ReceiveMeasurement", measurement);
        
        try
        {
            await _measurementService.SaveMeasurementAsync(measurement);
            return Ok();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error saving measurement");
            return StatusCode(500, "Error saving measurement");
        }
    }
}

The Blazor Server application receives this broadcasted data and displays it on the Index.razor page.

@page "/"
@using WeighingDeviceDashboard.Service
@inject WeighingHubConnectionService WeighingHubConnectionService
@implements IDisposable
@using Radzen
@using Radzen.Blazor

<style>
    /* CSS styles omitted for brevity */
</style>

<div class="container">
    <div class="row">
        @for (int i = 0; i < 12; i++)
        {
            var clientId = registeredClients.ElementAtOrDefault(i) ?? string.Empty;
            var hasData = !string.IsNullOrEmpty(clientId) && _currentMeasurements.ContainsKey(clientId);
            <div class="col-lg-3 col-md-4 col-sm-6">
                <div class="client-card @(hasData? "has-data" : "") @(hasData && newlyUpdatedClientId == clientId? "blink-border" : "")">
                    <div class="text-left">
                        <RadzenText Text="@clientId" Class="client-id" />
                    </div>
                    @if (hasData)
                    {
                        <RadzenText Text="@_currentMeasurements[clientId]" Class="weight-data" />
                        <RadzenText Text="@latestMeasurements[clientId].ToString("dd.MM.yyyy HH:mm")" Class="radzen-text" />
                        <div class="bottom-info">
                            <div class="flex-container">
                                <RadzenText Text="@_vehicleTypes[clientId]" Class="radzen-text" Style="margin-right: 0.5rem;" />
                                <RadzenText Text="@_plateNumbers[clientId]" Class="radzen-text" Style="margin-left: 0.5rem;" />
                            </div>
                        </div>
                    }
                    else
                    {
                        <div class="no-data-container">
                            <RadzenText Text="No Data" Style="font-size: 1.2em; font-weight: normal; color: #6c757d;" Class="radzen-text" />
                        </div>
                    }
                </div>
            </div>
        }
    </div>
</div>

@code {
    // C# code omitted for brevity
}

However, when navigating to the weighing-history page and returning to the Index page, no data is displayed.

@page "/weighing-history"
@using Radzen
@using Radzen.Blazor
@using WeighingDeviceDashboard.Service
@inject IMeasurementService MeasurementService
@inject WeighingHubConnectionService WeighingHubConnectionService

<RadzenDataGrid @ref=grid Data="@weighingData" TItem="Measurement" AllowFiltering="true" AllowColumnResize="false" AllowAlternatingRows="true"
                FilterMode="FilterMode.Advanced" AllowSorting="true" PageSize="15" AllowPaging="true" AllowGrouping="true" 
                ShowPagingSummary="true" ColumnWidth="300px" LogicalFilterOperator="LogicalFilterOperator.Or"
                IsLoading=@isLoading Sort="@ShowLoading" Page="@ShowLoading" Group="@ShowLoading" Filter="@ShowLoading">
    <Columns>
        <RadzenDataGridColumn Property="@nameof(Measurement.Id)" Title="Id" Width="160px" Visible="false" />
        <RadzenDataGridColumn Property="@nameof(Measurement.ClientId)" Title="Client ID" Width="200px" />
        <RadzenDataGridColumn Property="@nameof(Measurement.Value)" Title="Weight" Width="160px" />
        <RadzenDataGridColumn Property="@nameof(Measurement.Unit)" Title="Unit" Width="100px" />
        <RadzenDataGridColumn Property="@nameof(Measurement.Timestamp)" Title="Weight Date Time" Width="200px" />
        <RadzenDataGridColumn Property="@nameof(Measurement.VehicleType)" Title="Vehicle Type" Width="200px" />
        <RadzenDataGridColumn Property="@nameof(Measurement.PlateNumber)" Title="Plate Number" Width="200px" />
    </Columns>
</RadzenDataGrid>

@code {
    // C# code omitted for brevity
}

I would appreciate guidance on the following:

  1. How to maintain the displayed data on the Index page after navigating away?
  2. How to receive the broadcasted data in the grid without needing to refresh the page?

Thank you.

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,645 questions
Blazor
Blazor
A free and open-source web framework that enables developers to create web apps using C# and HTML being developed by Microsoft.
1,605 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Bruce (SqlWork.com) 67,406 Reputation points
    2024-11-19T16:54:29.55+00:00
    1. you can move the data to a common parent and cascade to the required components. or just query the database to get historial
    2. Blazor is a compoent based architecture with a render tree. to update the dom, a compoent refreshes, updating the render tree. to add data to a grid you re-render the displayed grid. if its a large grid, then you want to use one that supports a virtual window, so the number of displayed items is small.

    note: the assumption behind virtual tree component systems, is that rendering a component is quick. also you should avoid rendering non-visible content.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.