在实际业务中,当后台数据发生变化,客户端能够实时的收到通知,而不是由用户主动的进行页面刷新才能查看,这将是一个非常人性化的设计。比如数字化大屏,并没有人工的干预,而是自动的刷新数据,那如何才能实现数据的实时刷新呢?本文以一个简单示例,简述如何通过WPF+ASP.NET SignalR实现消息后台通知以及数据的实时刷新,仅供学习分享使用,如有不足之处,还请指正。
通过上一篇文章的学习,了解了如何通过SignalR实现在线聊天功能,在示例中,我们发现每一次的客户端连接都是一个新的实例对象,所以没有办法在中心对象中存储状态信息,所以为了存储用户列表,我们采用了静态变量的方式。并且在线聊天功能是用户发送一条消息(Chat),然后触发中心对象(ChatHub),转发给另一个用户(SendAsync)。那么如果实现数字化大屏,需要服务端持续的往客户端发送消息,而不是客户端主动触发,应该怎么做呢?这就是本文需要分享的内容。
在本示例中,涉及知识点如下所示:
实现服务端持续往客户端发送消息,除了业务上的需求外,还需要满足两个条件:
满足以上两个条件,才可以实现想要的功能。
经过以上分析后,服务端分为两方面,核心对象(ChatHub),处理业务对象(Worker)。下面我们逐一说明:
ChatHub 中心是用于向连接到 SignalR 服务器的客户端发送消息的核心抽象,负责客户端的连接和断开。如下所示:
- using Microsoft.AspNetCore.SignalR;
-
- namespace SignalRChat.Chat
- {
- public class ChatHub:Hub
- {
- public override Task OnConnectedAsync()
- {
- Console.WriteLine($"ID:{Context.ConnectionId} 已连接");
- return base.OnConnectedAsync();
- }
-
- public override Task OnDisconnectedAsync(Exception? exception)
- {
- Console.WriteLine($"ID:{Context.ConnectionId} 已断开");
- return base.OnDisconnectedAsync(exception);
- }
- }
- }
Worker实例为一个单例对象,常驻内容,实时监听数据变化,并通过ChatHub上下文(IHubContext
- using Microsoft.AspNetCore.SignalR;
-
- namespace SignalRChat.Chat
- {
- public class Worker
- {
- public static Worker Instance;
-
- private static readonly object locker=new object();
-
- private IHubContext
context; -
- private System.Timers.Timer timer;
-
- public Worker(IHubContext
context ) { - this.context = context;
- timer= new System.Timers.Timer(500);//单位毫秒
- timer.Enabled=true;
- timer.AutoReset=true;//自动重新
- timer.Elapsed += Timer_Elapsed;
- timer.Start();
- }
-
- private void Timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
- {
- //模拟数据,一般情况下,从数据库获取,然后通知到客户端
- Dictionary<string, object> data = new Dictionary<string, object>();
- var online = new Random().Next(0, 100);
- var male = Math.Floor(new Random().NextSingle() * online);
- var female = online - male;
- data["online"]=online;
- data["male"] =male;
- data["female"] = female;
- context.Clients.All.SendAsync("Data",data);
- }
-
- public static void Register(IHubContext
context ) - {
- if (Instance == null)
- {
- lock (locker)
- {
- if (Instance == null)
- {
- Instance = new Worker(context);
- }
- }
- }
- }
- }
- }
注意:此处发送数据的是Data方法,客户端必须监听Data方法,才能接收数据。
如何创建单例对象呢,中心对象上下文不能自己创建,必须要和ChatHub通过注入方式的上下文是同一个,不然无法获取客户端连接信息。在项目启动时,通过中间件的方式创建,如下所示:
- using Microsoft.AspNetCore.SignalR;
- using SignalRChat.Chat;
-
- var builder = WebApplication.CreateBuilder(args);
-
- // Add services to the container.
-
- builder.Services.AddControllers();
- // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
- builder.Services.AddEndpointsApiExplorer();
- builder.Services.AddSwaggerGen();
- //1.添加SignalR服务
- builder.Services.AddSignalR();
- var app = builder.Build();
-
- // Configure the HTTP request pipeline.
- if (app.Environment.IsDevelopment())
- {
- app.UseSwagger();
- app.UseSwaggerUI();
- }
- app.UseRouting();
- app.UseHttpsRedirection();
-
- app.UseAuthorization();
- //在Use中注册单例实例
- app.Use(async (context, next) =>
- {
- var hubContext = context.RequestServices
- .GetRequiredService
>(); - Worker.Register(hubContext);//调用静态方法注册
-
- if (next != null)
- {
- await next.Invoke();
- }
- });
- app.MapControllers();
- //2.映射路由
- app.UseEndpoints(endpoints => {
- endpoints.MapHub
("/chat"); - });
-
- app.Run();
客户端主要是连接服务器,然后监听服务端发送数据的方法即可,如下所示:
- namespace SignalRClient
- {
- public class ShowDataViewModel : ObservableObject
- {
- #region 属性及构造函数
-
- private int online;
-
- public int Online
- {
- get { return online; }
- set { SetProperty(ref online, value); }
- }
-
- private int male;
-
- public int Male
- {
- get { return male; }
- set { SetProperty(ref male, value); }
- }
-
-
- private int female;
-
- public int Female
- {
- get { return female; }
- set { SetProperty(ref female, value); }
- }
-
- private HubConnection hubConnection;
-
- public ShowDataViewModel()
- {
-
- }
-
- #endregion
-
- #region 命令
-
- private ICommand loadedCommand;
-
- public ICommand LoadedCommand
- {
- get
- {
- if (loadedCommand == null)
- {
- loadedCommand = new RelayCommand<object>(Loaded);
- }
- return loadedCommand;
- }
- }
-
- private void Loaded(object obj)
- {
- //1.初始化
- InitInfo();
- //2.监听
- Listen();
- //3.连接
- Link();
- }
-
- #endregion
-
- ///
- /// 初始化Connection对象
- ///
- private void InitInfo()
- {
- hubConnection = new HubConnectionBuilder().WithUrl("https://localhost:7149/chat").WithAutomaticReconnect().Build();
- hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(5);
- }
-
- ///
- /// 监听
- ///
- private void Listen()
- {
- hubConnection.On
string,object>>("Data", ReceiveInfos); - }
-
- ///
- /// 连接
- ///
- private async void Link()
- {
- try
- {
- await hubConnection.StartAsync();
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.Message);
- }
- }
-
- private void ReceiveInfos(Dictionary<string, object> data)
- {
- if (data == null || data.Count < 1)
- {
- return;
- }
- int.TryParse(data["online"]?.ToString(),out int online);
- int.TryParse(data["male"]?.ToString(),out int male);
- int.TryParse(data["female"]?.ToString(),out int female);
- this.Online=online;
- this.Male = male;
- this.Female=female;
- }
- }
- }
注意:监听Data方法,和服务端发送时保持一致。
在示例中,需要同时启动服务端和客户端,所以以多项目方式启动,如下所示:
运行成功后,服务端以ASP.NET Web API的方式呈现,如下所示:
客户端运行如下:
注意:客户端可以有多个,也可以是一个,后台通知消息,会通知到每一个连接的客户端。
关注微信公众号,然后发送信息SignalR即可获取下载链接。如下所示:
以上就是WPF+ASP.NET SignalR实现后台实时通知的全部内容,关于SignalR的应用,实际场景有很多,这只是一个简单的入门示例,希望可以抛砖引玉,一起学习,共同进步。学习编程,从关注【老码识途】开始!!!