WPF+ASP.NET SignalR實(shí)現(xiàn)后臺(tái)通知功能的示例代碼
在實(shí)際業(yè)務(wù)中,當(dāng)后臺(tái)數(shù)據(jù)發(fā)生變化,客戶端能夠?qū)崟r(shí)的收到通知,而不是由用戶主動(dòng)的進(jìn)行頁(yè)面刷新才能查看,這將是一個(gè)非常人性化的設(shè)計(jì)。比如數(shù)字化大屏,并沒有人工的干預(yù),而是自動(dòng)的刷新數(shù)據(jù),那如何才能實(shí)現(xiàn)數(shù)據(jù)的實(shí)時(shí)刷新呢?本文以一個(gè)簡(jiǎn)單示例,簡(jiǎn)述如何通過WPF+ASP.NET SignalR實(shí)現(xiàn)消息后臺(tái)通知以及數(shù)據(jù)的實(shí)時(shí)刷新,僅供學(xué)習(xí)分享使用,如有不足之處,還請(qǐng)指正。
通過上一篇文章的學(xué)習(xí),了解了如何通過SignalR實(shí)現(xiàn)在線聊天功能,在示例中,我們發(fā)現(xiàn)每一次的客戶端連接都是一個(gè)新的實(shí)例對(duì)象,所以沒有辦法在中心對(duì)象中存儲(chǔ)狀態(tài)信息,所以為了存儲(chǔ)用戶列表,我們采用了靜態(tài)變量的方式。并且在線聊天功能是用戶發(fā)送一條消息(Chat),然后觸發(fā)中心對(duì)象(ChatHub),轉(zhuǎn)發(fā)給另一個(gè)用戶(SendAsync)。那么如果實(shí)現(xiàn)數(shù)字化大屏,需要服務(wù)端持續(xù)的往客戶端發(fā)送消息,而不是客戶端主動(dòng)觸發(fā),應(yīng)該怎么做呢?這就是本文需要分享的內(nèi)容。
涉及知識(shí)點(diǎn)
在本示例中,涉及知識(shí)點(diǎn)如下所示:
- 開發(fā)工具:Visual Studio 2022 目標(biāo)框架:.NET6.0
- ASP.NET SignalR,一個(gè)ASP .NET 下的類庫(kù),可以在ASP .NET 的Web項(xiàng)目中實(shí)現(xiàn)實(shí)時(shí)通信,目前新版已支持.NET6.0及以上版本。在本示例中,作為消息通知的服務(wù)端。
- WPF,是微軟推出的基于Windows 的用戶界面框架,主要用于開發(fā)客戶端程序。
前提條件
實(shí)現(xiàn)服務(wù)端持續(xù)往客戶端發(fā)送消息,除了業(yè)務(wù)上的需求外,還需要滿足兩個(gè)條件:
- 在服務(wù)端有一個(gè)常駐內(nèi)存對(duì)象,監(jiān)聽數(shù)據(jù)變化。
- 常駐內(nèi)存對(duì)象,可以訪問中心對(duì)象(ChatHub),能夠獲取中心對(duì)象的所有連接客戶端,并發(fā)送消息。
滿足以上兩個(gè)條件,才可以實(shí)現(xiàn)想要的功能。
服務(wù)端
經(jīng)過以上分析后,服務(wù)端分為兩方面,核心對(duì)象(ChatHub),處理業(yè)務(wù)對(duì)象(Worker)。下面我們逐一說明:
ChatHub 中心是用于向連接到 SignalR 服務(wù)器的客戶端發(fā)送消息的核心抽象,負(fù)責(zé)客戶端的連接和斷開。如下所示:
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實(shí)例為一個(gè)單例對(duì)象,常駐內(nèi)容,實(shí)時(shí)監(jiān)聽數(shù)據(jù)變化,并通過ChatHub上下文(IHubContext<ChatHub>)獲取連接信息,然后發(fā)送消息,如下所示:
using Microsoft.AspNetCore.SignalR; namespace SignalRChat.Chat { public class Worker { public static Worker Instance; private static readonly object locker=new object(); private IHubContext<ChatHub> context; private System.Timers.Timer timer; public Worker(IHubContext<ChatHub> context) { this.context = context; timer= new System.Timers.Timer(500);//單位毫秒 timer.Enabled=true; timer.AutoReset=true;//自動(dòng)重新 timer.Elapsed += Timer_Elapsed; timer.Start(); } private void Timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e) { //模擬數(shù)據(jù),一般情況下,從數(shù)據(jù)庫(kù)獲取,然后通知到客戶端 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<ChatHub> context) { if (Instance == null) { lock (locker) { if (Instance == null) { Instance = new Worker(context); } } } } } }
注意:此處發(fā)送數(shù)據(jù)的是Data方法,客戶端必須監(jiān)聽Data方法,才能接收數(shù)據(jù)。
如何創(chuàng)建單例對(duì)象呢,中心對(duì)象上下文不能自己創(chuàng)建,必須要和ChatHub通過注入方式的上下文是同一個(gè),不然無法獲取客戶端連接信息。在項(xiàng)目啟動(dòng)時(shí),通過中間件的方式創(chuàng)建,如下所示:
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服務(wù) 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中注冊(cè)單例實(shí)例 app.Use(async (context, next) => { var hubContext = context.RequestServices .GetRequiredService<IHubContext<ChatHub>>(); Worker.Register(hubContext);//調(diào)用靜態(tài)方法注冊(cè) if (next != null) { await next.Invoke(); } }); app.MapControllers(); //2.映射路由 app.UseEndpoints(endpoints => { endpoints.MapHub<ChatHub>("/chat"); }); app.Run();
客戶端
客戶端主要是連接服務(wù)器,然后監(jiān)聽服務(wù)端發(fā)送數(shù)據(jù)的方法即可,如下所示:
namespace SignalRClient { public class ShowDataViewModel : ObservableObject { #region 屬性及構(gòu)造函數(shù) 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.監(jiān)聽 Listen(); //3.連接 Link(); } #endregion /// <summary> /// 初始化Connection對(duì)象 /// </summary> private void InitInfo() { hubConnection = new HubConnectionBuilder().WithUrl("https://localhost:7149/chat").WithAutomaticReconnect().Build(); hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(5); } /// <summary> /// 監(jiān)聽 /// </summary> private void Listen() { hubConnection.On<Dictionary<string,object>>("Data", ReceiveInfos); } /// <summary> /// 連接 /// </summary> 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; } } }
注意:監(jiān)聽Data方法,和服務(wù)端發(fā)送時(shí)保持一致。
運(yùn)行示例
在示例中,需要同時(shí)啟動(dòng)服務(wù)端和客戶端,所以以多項(xiàng)目方式啟動(dòng),如下所示:
運(yùn)行成功后,服務(wù)端以ASP.NET Web API的方式呈現(xiàn),如下所示:
客戶端運(yùn)行如下:
注意:客戶端可以有多個(gè),也可以是一個(gè),后臺(tái)通知消息,會(huì)通知到每一個(gè)連接的客戶端。
到此這篇關(guān)于WPF+ASP.NET SignalR實(shí)現(xiàn)后臺(tái)通知功能的示例代碼的文章就介紹到這了,更多相關(guān)WPF后臺(tái)通知內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#?使用EntityFramework?CodeFirst?創(chuàng)建PostgreSQL數(shù)據(jù)庫(kù)的詳細(xì)過程
這篇文章主要介紹了C#使用EntityFramework?CodeFirst創(chuàng)建PostgreSQL數(shù)據(jù)庫(kù)的過程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07運(yùn)用示例簡(jiǎn)單講解C#取消令牌CancellationTokenSource
這篇文章運(yùn)用示例簡(jiǎn)單講解C#取消令牌CancellationTokenSource,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08C#連接db2數(shù)據(jù)庫(kù)的實(shí)現(xiàn)方法
本篇文章是對(duì)C#連接db2數(shù)據(jù)庫(kù)的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05c#中DataTable轉(zhuǎn)List的2種方法示例
這篇文章主要給大家介紹了關(guān)于c#中DataTable轉(zhuǎn)List的2種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04