You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor结合SignalR无法从其他页面刷新组件的问题求助

解决SignalR仅更新当前用户(含多标签页)状态且Blazor组件不刷新的问题

我被这个问题困扰多日,查阅官方SignalR Hub文档仍无法解决。使用Clients.Caller或Clients.Client时无法更新Blazor组件,Clients.All虽能更新但会影响所有连接客户端,而我仅需更新当前用户/访客的状态却无法实现,包括同一用户的多个浏览器标签页也无法同步更新。

服务器端Hub修复代码

using DocZ.Data;
using DocZ.Helpers;
using DocZ.Models.Security;
using DocZ.Services.Data;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BlazorWebAssemblySignalRApp.Server.Hubs
{
    public class BannerMyUsageHub : Hub
    {
        private readonly ILogger<BannerMyUsageHub> _logger;
        private readonly IDatabaseService _databaseService;
        // 线程安全的用户-连接ID映射表,存储同一用户的所有活跃连接
        private static readonly Dictionary<string, HashSet<string>> _userConnections = new Dictionary<string, HashSet<string>>();

        public BannerMyUsageHub(IDbContextFactory<ApplicationDbContext> dbContextFactory, IDatabaseService databaseService, ILogger<BannerMyUsageHub> logger)
        {
            _logger = logger;
            _databaseService = databaseService;
        }

        public override async Task OnConnectedAsync()
        {
            var userName = Context.User?.Identity?.Name;
            if (!string.IsNullOrEmpty(userName))
            {
                lock (_userConnections)
                {
                    if (!_userConnections.ContainsKey(userName))
                    {
                        _userConnections[userName] = new HashSet<string>();
                    }
                    _userConnections[userName].Add(Context.ConnectionId);
                }
            }
            await base.OnConnectedAsync();
        }

        public override async Task OnDisconnectedAsync(Exception exception)
        {
            var userName = Context.User?.Identity?.Name;
            if (!string.IsNullOrEmpty(userName))
            {
                lock (_userConnections)
                {
                    if (_userConnections.TryGetValue(userName, out var connections))
                    {
                        connections.Remove(Context.ConnectionId);
                        if (connections.Count == 0)
                        {
                            _userConnections.Remove(userName);
                        }
                    }
                }
            }
            await base.OnDisconnectedAsync(exception);
        }

        /// <summary>
        /// 接收客户端或后端的SignalR请求,更新指定用户的状态
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public async Task MyUsageMessageTouserName(string userName)
        {
            if (!String.IsNullOrEmpty(userName))
            {
                // 从数据库获取待处理记录数
                int totalMyUsage = await _databaseService.BannerMyUsageCountPending(userName);
                string messageToViewer = totalMyUsage > 0 
                    ? $"You have {totalMyUsage} open chart{UtilityMethods.Pluralization(totalMyUsage)}." 
                    : "";

                // 发送给该用户的所有活跃连接(覆盖多标签页)
                lock (_userConnections)
                {
                    if (_userConnections.TryGetValue(userName, out var connectionIds))
                    {
                        foreach (var connectionId in connectionIds)
                        {
                            await Clients.Client(connectionId).SendAsync("ReceiveMessage", messageToViewer);
                        }
                    }
                }
            }
        }
    }
}

核心解决步骤

  • 维护用户连接映射
    通过静态字典记录每个用户的所有活跃连接ID,在OnConnectedAsync和OnDisconnectedAsync中同步连接状态,解决同一用户多标签页无法接收消息的问题。

  • 确保身份关联正确
    Blazor客户端连接SignalR时必须传递认证令牌,保证服务器端Context.User.Identity.Name与业务逻辑中的userName一致:

    // Blazor客户端连接示例
    var connection = new HubConnectionBuilder()
        .WithUrl(NavigationManager.ToAbsoluteUri("/bannerMyUsageHub"), options =>
        {
            options.AccessTokenProvider = () => Task.FromResult(await localStorage.GetItemAsync<string>("authToken"));
        })
        .Build();
    
  • 强制Blazor组件刷新
    客户端接收消息时,必须在UI线程触发组件状态更新:

    // Blazor组件内的消息处理逻辑
    protected override async Task OnInitializedAsync()
    {
        await hubConnection.StartAsync();
        hubConnection.On<string>("ReceiveMessage", async (message) =>
        {
            MyUsageMessage = message;
            // 确保在UI线程执行刷新
            await InvokeAsync(() => StateHasChanged());
        });
    }
    
  • 替代Clients.User方案
    若Clients.User(userName)无效,本质是身份关联不匹配,自定义连接映射是更可靠的精准推送方式,直接通过连接ID发送消息。

内容的提问来源于stack exchange,提问作者Ai Vay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 11:25:02