Blazor component is not rendering when singlar notification is recevied

Balaji Mogadali 80 Reputation points
2025-09-25T02:56:02.53+00:00

I receive a singal R notification from azure blob storage
Issue: Razor page is not refreshing , even it get called by InvokeAsync

Razor page 

@page "/cloudstatus"
@inject SignalRNotificationService signalRNotificationService
@inject HttpClient Http
<h3>Azure Dashboard</h3>
<p>Notification count: @notifications.Count</p>
<ul>
    @foreach (var notification in notifications)
    {
        <li>@notification.Title: @notification.Message</li>
    }
</ul>
@code {
    private List<SignalRNotificationMessage> notifications = new();
    protected override async Task OnInitializedAsync()
    {
        // Get connection info from negotiate endpoint
        var connectionInfo = await Http.PostAsJsonAsync("[http://localhost:7283/api/negotiate]", new { });
        var info = await connectionInfo.Content.ReadFromJsonAsync<SignalRConnectionInfo>();
        // Use named method for event handler
        signalRNotificationService.OnNotificationReceived += HandleNotificationReceived;
        await signalRNotificationService.StartAsync(info.Url, info.AccessToken);
        Console.WriteLine("SignalR connection state: " + signalRNotificationService.ConnectionState);
    }
    private Task HandleNotificationReceived(SignalRNotificationMessage notification)
    {
        return  InvokeAsync(async () =>
        {
            try
            {
                Console.WriteLine($"Received: {notification.Message}");
                notifications.Add(notification);
                Console.WriteLine($"Notification count: {notifications.Count}");
                await InvokeAsync(StateHasChanged);
                await OnAfterRenderAsync(true);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error in UI update: " + ex.Message);
            }
        });
     
    }
    protected override async Task OnAfterRenderAsync(bool firstRender){
        if (firstRender)
        {
            await InvokeAsync(StateHasChanged);
        }
    }
}

SignalRNotificationService

using Microsoft.AspNetCore.SignalR.Client;
using Radzen;
namespace AIResumeScanner_Razden.Models
{
    public class SignalRNotificationService
    {
        private HubConnection _connection;
        public HubConnectionState ConnectionState => _connection.State;
        //public event Action<SignalRNotificationMessage> OnNotificationReceived;
        public event Func<SignalRNotificationMessage, Task>? OnNotificationReceived;
        public async Task StartAsync(string hubUrl, string accessToken)
        {
            _connection = new HubConnectionBuilder()
                .WithUrl(hubUrl, options =>
                {
                    options.AccessTokenProvider = () => Task.FromResult(accessToken);
                })
                .WithAutomaticReconnect()
                .Build();
            _connection.On<SignalRNotificationMessage>("ReceiveNotification", async (notification) =>
            {
                if (OnNotificationReceived != null)
                    await OnNotificationReceived?.Invoke(notification);                
            });
            await _connection.StartAsync();
        }
        public async Task StopAsync()
        {
            if (_connection != null)
                await _connection.StopAsync();
        }
    }
}
  • Program.cs
using AIResumeScanner_Razden.Components;
using AIResumeScanner_Razden.Services;
using AIResumeScanner_Razden.Models;
using Radzen;
namespace AIResumeScanner_Razden
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            builder.Services.AddRazorPages();
           
            // Add services to the container.
            builder.Services.AddRazorComponents()
                .AddInteractiveServerComponents();
            builder.Services.AddRadzenComponents();
            builder.Services.AddHttpClient(); // Registers IHttpClientFactory
            builder.Services.AddSingleton<SignalRNotificationService>();
            builder.Services.AddSingleton<SentimentService>();
            builder.Services.AddSingleton(sp =>
                                                builder.Configuration.GetSection("ApiSettings").Get<ApiSettings>());
            builder.Services.AddSignalR(
                                        options =>
                                        {
                                            options.EnableDetailedErrors = true;
                                            options.MaximumReceiveMessageSize = 1024 * 1024;
                                        });
            var app = builder.Build();
            // Configure the HTTP request pipeline.
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
         
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAntiforgery();
            app.MapRazorComponents<App>()
                .AddInteractiveServerRenderMode();
           
            app.MapRazorPages();
         
            app.Run();
        }
    }
}
Developer technologies | ASP.NET | ASP.NET Core
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Raymond Huynh (WICLOUD CORPORATION) 2,325 Reputation points Microsoft External Staff Moderator
    2025-09-25T09:21:34.4333333+00:00

    Hello Balaji Mogadali,

    This is a threading issue with Blazor and SignalR integration. The problem occurs because SignalR events come in on a different thread than Blazor's UI thread, which can cause rendering issues.

    Looking at your code, the main issue is in your HandleNotificationReceived method. Here are the key changes that should fix the rendering problem:

    private Task HandleNotificationReceived(SignalRNotificationMessage notification)
    {
        return InvokeAsync(() =>
        {
            try
            {
                Console.WriteLine($"Received: {notification.Message}");
                notifications.Add(notification);
                Console.WriteLine($"Notification count: {notifications.Count}");
                StateHasChanged(); // Move this inside InvokeAsync, remove the await
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error in UI update: " + ex.Message);
            }
        });
    }
    

    The key changes:

    1. Remove the async and await from inside the InvokeAsync - you don't need them here
    2. Move StateHasChanged() directly inside the InvokeAsync without the extra await InvokeAsync(StateHasChanged)
    3. Remove the call to OnAfterRenderAsync(true) - that's not needed for this scenario

    Also, I noticed in your OnAfterRenderAsync method, you're calling StateHasChanged() again on first render. You can simplify that to:

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // Only do first-render specific tasks here if needed
        // StateHasChanged() isn't necessary here
    }
    

    One more thing to check, make sure your SignalR connection is actually established before trying to update the UI. You might want to add a check like:

    if (signalRNotificationService.ConnectionState == HubConnectionState.Connected)
    {
        // Your notification handling logic here
    }
    

    The threading context is really important with Blazor Server, SignalR events come in on a different thread, so InvokeAsync ensures your UI updates happen on the correct thread that Blazor's renderer expects.

    Hope this helps!


Your answer

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