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

ASP.NET MVC+Vue.js项目中,如何通过SignalR基于examId向指定客户端推数?

需求实现方案:ASP.NET MVC+Vue.js 结合 SignalR 实现实时推送患者检查数据

这个需求完全可以通过SignalR实现,而且是这类实时推送场景的最优方案,下面是具体实现思路和步骤:

核心逻辑

  1. 用SignalR建立服务器与Vue客户端的实时连接,客户端打开对应examId的页面时,向服务器注册自己关注的examId
  2. 服务器监听外部数据库的exam表新增事件,一旦检测到新数据,通过SignalR精准推送给所有关注该examId的客户端
  3. Vue客户端收到推送后,自动更新页面数据,无需用户手动操作

具体实现步骤

1. 配置SignalR Hub

首先在ASP.NET MVC项目中创建SignalR Hub,维护客户端连接与examId的映射关系:

using Microsoft.AspNetCore.SignalR;
using System.Collections.Concurrent;

public class ExamHub : Hub
{
    // 存储examId与对应连接ID的映射
    private static readonly ConcurrentDictionary<int, HashSet<string>> _examConnections = new();

    // 客户端连接时注册关注的examId
    public async Task RegisterExam(int examId)
    {
        var connectionId = Context.ConnectionId;
        // 线程安全地添加连接到对应examId的集合
        _examConnections.AddOrUpdate(examId, 
            new HashSet<string> { connectionId }, 
            (key, existing) => { existing.Add(connectionId); return existing; });
        
        await Clients.Caller.SendAsync("RegisteredSuccessfully", examId);
    }

    // 客户端断开连接时清理映射
    public override async Task OnDisconnectedAsync(Exception exception)
    {
        // 遍历所有examId,移除当前连接ID
        foreach (var kvp in _examConnections)
        {
            kvp.Value.Remove(Context.ConnectionId);
            if (kvp.Value.Count == 0)
            {
                _examConnections.TryRemove(kvp.Key, out _);
            }
        }
        await base.OnDisconnectedAsync(exception);
    }

    // 服务器主动推送更新给对应examId的客户端
    public async Task SendExamUpdate(int examId, object examData)
    {
        if (_examConnections.TryGetValue(examId, out var connectionIds))
        {
            await Clients.Clients(connectionIds).SendAsync("ReceiveExamUpdate", examData);
        }
    }
}

然后在Program.cs中注册SignalR服务并配置路由:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSignalR();

var app = builder.Build();
app.MapHub<ExamHub>("/examHub");
// ... 其他中间件配置

2. 监听数据库新增事件

针对外部数据库的新增检测,根据数据库类型选择合适的方式:

  • SQL Server:推荐使用Change Tracking(轻量易实现),或者Service Broker+触发器实现实时通知。示例用Change Tracking结合后台任务轮询检测:
    using Microsoft.AspNetCore.SignalR;
    using Microsoft.EntityFrameworkCore;
    
    public class ExamChangeMonitor : BackgroundService
    {
        private readonly IServiceScopeFactory _scopeFactory;
        private readonly IHubContext<ExamHub> _hubContext;
        private DateTime _lastCheckTime;
    
        public ExamChangeMonitor(IServiceScopeFactory scopeFactory, IHubContext<ExamHub> hubContext)
        {
            _scopeFactory = scopeFactory;
            _hubContext = hubContext;
            _lastCheckTime = DateTime.UtcNow;
        }
    
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                using var scope = _scopeFactory.CreateScope();
                var dbContext = scope.ServiceProvider.GetRequiredService<ExternalDbContext>();
                
                // 查询上次检查后新增的exam数据
                var newExams = await dbContext.Exams
                    .Where(e => e.CreationTime > _lastCheckTime)
                    .ToListAsync(stoppingToken);
    
                foreach (var exam in newExams)
                {
                    // 调用Hub推送数据给对应examId的客户端
                    await _hubContext.Clients.Clients(_examConnections[exam.Id]).SendAsync("ReceiveExamUpdate", exam);
                }
    
                _lastCheckTime = DateTime.UtcNow;
                // 每5秒轮询一次,可根据需求调整
                await Task.Delay(5000, stoppingToken);
            }
        }
    }
    
    记得在Program.cs中注册这个后台服务:
    builder.Services.AddHostedService<ExamChangeMonitor>();
    
  • 其他数据库:可以用定时轮询(类似上面的方式),或者借助数据库自带的通知机制(比如PostgreSQL的LISTEN/NOTIFY)

3. Vue客户端实现连接与接收

在Vue项目中引入@microsoft/signalr库,然后在对应页面组件中实现连接:

import * as signalR from '@microsoft/signalr';

export default {
  data() {
    return {
      connection: null,
      currentExamId: this.$route.params.examId, // 从路由获取当前examId
      examData: {}
    };
  },
  mounted() {
    this.initSignalR();
  },
  beforeUnmount() {
    this.connection?.stop();
  },
  methods: {
    async initSignalR() {
      this.connection = new signalR.HubConnectionBuilder()
        .withUrl('/examHub')
        .build();

      // 接收服务器推送的更新
      this.connection.on('ReceiveExamUpdate', (data) => {
        this.examData = data;
        // 触发页面更新逻辑,比如刷新详情或表格
      });

      // 连接成功后注册当前examId
      this.connection.on('RegisteredSuccessfully', (examId) => {
        console.log(`已注册关注examId: ${examId}`);
      });

      try {
        await this.connection.start();
        await this.connection.invoke('RegisterExam', this.currentExamId);
      } catch (err) {
        console.error('SignalR连接失败:', err);
        // 失败后自动重试
        setTimeout(() => this.initSignalR(), 5000);
      }
    }
  }
};

备选方案(不推荐)

如果由于限制不能使用SignalR,也可以手动实现:

  • WebSocket:自行建立WebSocket连接,维护连接映射和推送逻辑,复杂度较高
  • Server-Sent Events (SSE):仅支持单向推送,兼容性不如SignalR

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 12:18:13