WPF+ASP.NET SignalR實現動態折線圖

来源:https://www.cnblogs.com/hsiang/archive/2023/01/02/17020265.html
-Advertisement-
Play Games

在實際業務中,當後臺數據發生變化,客戶端能夠實時的收到通知,而不是由用戶主動的進行頁面刷新才能查看,這將是一個非常人性化的設計。有沒有那麼一種場景,後臺數據明明已經發生變化了,前臺卻因為沒有及時刷新,而導致頁面顯示的數據與實際存在差異,從而造成錯誤的判斷。那麼如何才能在後臺數據變更時及時通知客戶端呢... ...


在實際業務中,當後臺數據發生變化,客戶端能夠實時的收到通知,而不是由用戶主動的進行頁面刷新才能查看,這將是一個非常人性化的設計。有沒有那麼一種場景,後臺數據明明已經發生變化了,前臺卻因為沒有及時刷新,而導致頁面顯示的數據與實際存在差異,從而造成錯誤的判斷。那麼如何才能在後臺數據變更時及時通知客戶端呢?本文以一個簡單的動態折線圖示例,簡述如何通過ASP.NET SignalR實現後臺通知功能,僅供學習分享使用,如有不足之處,還請指正。

什麼是SignalR?

 ASP.NET SignalR 是一個面向 ASP.NET 開發人員的庫,可簡化將實時 web 功能添加到應用程式的過程。 實時 web 功能是讓伺服器代碼將內容推送到連接的客戶端立即可用,而不是讓伺服器等待客戶端請求新數據的能力。

SignalR做了什麼?

傳統HTTP採用的是大家熟知的“拉模式”,即客戶端發出的每次請求,服務端都是被動處理。此場景下客戶端是老大,很顯然只有一方主動,操作與處理起來就沒那麼完美。

為了能讓服務端也能主動,html5的出現讓這種變得可能,大家知道html5中有兩種主動模式。第一種叫做websockectWebSocketsHtml5提供的新的API,可以在Web網頁與伺服器端間建立Socket連接,它是基於tcp模式的雙工通訊。還有一種叫做SSE,也就是客戶端來訂閱伺服器的一種事件模型。

html5出來之前,如果要做到伺服器主動,我們只能採用變相的longpooliframe流勉強實現。

SignalR對上面四種方案進行了高度的封裝,也就是說signalR會在這四種技術中根據瀏覽器和伺服器設置採取最優的一種模式。

封裝與集成

對於.NET開發者的福音,.NET平臺為我們提供了一種簡潔高效智能的實時信息交互技術->SignalR,它集成了上述數種技術,並能根據配置自動或手動選擇其最佳應用。

SignalR用途

SignalR 提供了一個簡單的 API,用於創建伺服器到客戶端遠程過程調用 (RPC) ,該調用客戶端瀏覽器 (和其他客戶端平臺中的 JavaScript 函數) 伺服器端 .NET 代碼。 SignalR 還包括用於連接管理的 API (,例如連接和斷開連接事件) ,以及分組連接。

雖然聊天通常被用作示例,但你可以做更多的事情。每當用戶刷新網頁以查看新數據時,或者該網頁實施 Ajax 長輪詢以檢索新數據時,它都是使用 SignalR 的候選者。SignalR 還支持需要從伺服器進行高頻更新的全新類型的應用,例如實時游戲。

 

官方網址和源碼

 

官方網址:https://dotnet.microsoft.com/zh-cn/apps/aspnet/signalr

微軟API文檔:https://learn.microsoft.com/zh-cn/aspnet/signalr/overview/getting-started/introduction-to-signalr

GitHub網址:https://github.com/SignalR

示例截圖

本示例主要實現一個動態折線圖功能,主要分為服務端和客戶端兩部分,示例如下所示:

服務端項目創建

 

1. 創建一個Web服務端程式(以ASP.NET WebApi為例),預設情況下SignalR已經作為項目框架的一部分而存在,所以不需要安裝,直接使用即可。通過項目--依賴性--框架--Microsoft.AspNetCore.App可以查看

2. 創建ChatHub,繼承Hub基類,作為後臺連接管理的中心

 1 using Microsoft.AspNetCore.SignalR;
 2 
 3 namespace DemoSignalR.Server.Chat
 4 {
 5     public class ChatHub : Hub
 6     {
 7         #region 連接和斷開連接
 8 
 9         public override async Task OnConnectedAsync()
10         {
11             var connId = Context.ConnectionId;
12             Console.WriteLine($"{connId} 已連接");
13             await base.OnConnectedAsync();
14         }
15 
16         public void StartNotify(string type)
17         {
18             if (type == "1")
19             {
20 
21             }
22             else if (type == "2")
23             {
24 
25             };
26 
27         }
28 
29         public override async Task OnDisconnectedAsync(Exception ex)
30         {
31             //如果斷開連接
32             var connId = Context.ConnectionId;
33             Console.WriteLine($"{connId} 已斷開");
34             await base.OnDisconnectedAsync(ex);
35         }
36 
37         #endregion
38     }
39 }

SignalR服務端業務集成

在實際業務中,存在各種需要後臺通知的功能,根據不同的業務,可以採用不同的通知觸發方式:

1. 在調用介面時觸發後臺通知

 1 using DemoSignalR.Server.Chat;
 2 using Microsoft.AspNetCore.Mvc;
 3 using Microsoft.AspNetCore.SignalR;
 4 
 5 namespace DemoSignalR.Server.Controllers
 6 {
 7     [ApiController]
 8     [Route("[controller]")]
 9     public class TestWebApiController : ControllerBase
10     {
11 
12 
13         private readonly ILogger<TestWebApiController> _logger;
14 
15         private IHubContext<ChatHub> _context;
16 
17         public TestWebApiController(ILogger<TestWebApiController> logger, IHubContext<ChatHub> context)
18         {
19             _logger = logger;
20             _context = context;
21         }
22 
23         [HttpGet]
24         public void GetTestA(string Name)
25         {
26             string info = $"當前接收到的信息為:{Name},{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}";
27             _context.Clients.All.SendAsync("", info);
28         }
29 
30 
31     }
32 }

2. 定時器迴圈通知

 1 using Microsoft.AspNetCore.SignalR;
 2 using System.Timers;
 3 
 4 namespace DemoSignalR.Server.Chat
 5 {
 6     public class TestChatInfo
 7     {
 8         private IHubContext<ChatHub> _context;
 9 
10         private System.Timers.Timer _timer;
11 
12         private readonly static object locker = new object();//鎖對象
13 
14         public static TestChatInfo instance;//當前實例
15 
16         private readonly ILogger _logger;
17 
18         private int _index = 0;
19 
20         private TestChatInfo(IHubContext<ChatHub> _context, ILogger _logger)
21         {
22             this._context = _context;
23             this._logger = _logger;
24             //定義定時器
25             _timer = new System.Timers.Timer(100);
26             _timer.AutoReset = true;
27             _timer.Enabled = true;
28             _timer.Elapsed += Timer_Elapsed;
29             _timer.Start();
30         }
31 
32         private void Timer_Elapsed(object? sender, ElapsedEventArgs e)
33         {
34             //業務邏輯判斷
35             var obj = new TestValue();
36             obj.Index = this._index;
37             obj.Value = DateTime.Now.Millisecond % 100;
38             _context.Clients.All.SendAsync("RefreshInfos", obj);
39             this._index++;
40         }
41 
42         /// <summary>
43         /// 註冊,即初始化單例實例
44         /// </summary>
45         /// <param name="context"></param>
46         /// <param name="reviewTaskService"></param>
47         /// <param name="sysParamService"></param>
48         public static void Register(IHubContext<ChatHub> context, ILogger logger)
49         {
50             lock (locker)
51             {
52                 if (instance == null)
53                 {
54                     lock (locker)
55                     {
56                         instance = new TestChatInfo(context, logger);
57                     }
58                 }
59             }
60         }
61 
62     }
63 
64     public class TestValue
65     {
66         private int index;
67         public int Index { get { return index; } set { index = value; } }
68 
69         private float value;
70         public float Value { get { return value; } set { this.value = value; } }
71     }
72 }

 

SignalR服務端配置

 SignalR服務端配置主要分成三步:

1. 添加SignalR服務

2. 映射SignalR路由

3. 註冊單例後臺通知服務(如果其他方式,可省略)

 1 using DemoSignalR.Server.Chat;
 2 using Microsoft.AspNetCore.SignalR;
 3 
 4 var builder = WebApplication.CreateBuilder(args);
 5 
 6 // Add services to the container.
 7 
 8 builder.Services.AddControllers();
 9 // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
10 builder.Services.AddEndpointsApiExplorer();
11 builder.Services.AddSwaggerGen();
12 //1.添加SignalR服務
13 builder.Services.AddSignalR();
14 builder.Services.AddLogging(logging => logging.AddConsole());
15 
16 var app = builder.Build();
17 
18 // Configure the HTTP request pipeline.
19 if (app.Environment.IsDevelopment())
20 {
21     app.UseSwagger();
22     app.UseSwaggerUI();
23 }
24 app.UseRouting();
25 app.UseHttpsRedirection();
26 
27 app.UseAuthorization();
28 
29 
30 //在Use中註冊單例實例
31 app.Use(async (context, next) =>
32 {
33     var hubContext = context.RequestServices.GetRequiredService<IHubContext<ChatHub>>();
34     //var logger = context.RequestServices.GetRequiredService<ILogger>();
35     TestChatInfo.Register(hubContext, null);//調用靜態方法註冊
36     if (next != null)
37     {
38         await next.Invoke();
39     }
40 });
41 
42 app.MapControllers();
43 
44 //2.映射路由
45 app.UseEndpoints(endpoints => {
46     endpoints.MapHub<ChatHub>("/chat");
47 });
48 
49 app.Run();

客戶端項目創建

1. 創建WPF項目

2. 通過NuGet包管理器安裝SignalR客戶端

3. 創建SignalR狀態管理,主要管理SignalR的連接,關閉,重連等操作。

 1 using Microsoft.AspNetCore.SignalR.Client;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Configuration;
 5 using System.Linq;
 6 using System.Text;
 7 using System.Threading.Tasks;
 8 
 9 namespace DemoSignalR.Client
10 {
11     internal class SignalRClient
12     {
13         private readonly HubConnection hubConnection;
14 
15         public HubConnection HubConnection
16         {
17             get { return hubConnection; }
18         }
19 
20         public SignalRClient()
21         {
22             var server = ConfigurationManager.AppSettings["Server"].ToString();
23             hubConnection = new HubConnectionBuilder().WithUrl($"{server}/chat").WithAutomaticReconnect().Build();
24             hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(5);
25         }
26 
27         public virtual void Listen()
28         {
29 
30         }
31 
32         public async void Start()
33         {
34             try
35             {
36                 await hubConnection.StartAsync();
37 
38             }
39             catch (Exception e)
40             {
41 
42             }
43         }
44     }
45 }

客戶端業務邏輯處理

1. 根據不同的業務邏輯分別監聽不同的通知事件。

2. 示例詳見源碼

 1 using Microsoft.AspNetCore.SignalR.Client;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace DemoSignalR.Client
 9 {
10     internal class TestSignalRClient : SignalRClient
11     {
12         public Action<object> RefreshInfos;
13 
14         public Action<string> Reconnected;
15 
16         public TestSignalRClient() : base()
17         {
18         }
19 
20         public override void Listen()
21         {
22             HubConnection.On<object>("RefreshInfos", (obj) =>
23             {
24                 //
25                 if (obj != null)
26                 {
27                     Console.WriteLine("收到數據");
28                     //發送消息
29                     if (RefreshInfos != null)
30                     {
31                         RefreshInfos(obj);
32                     }
33                 }
34             });
35             HubConnection.Reconnected += HubConnection_Reconnected;
36         }
37 
38         private Task HubConnection_Reconnected(string arg)
39         {
40             return Task.Run(() =>
41             {
42                 if (Reconnected != null)
43                 {
44                     Reconnected(arg);
45                 }
46             });
47         }
48 
49         public virtual void StartNotify(string condition)
50         {
51             HubConnection.SendAsync("StartNotify", condition);
52             Console.WriteLine($"開始通過知{condition}");
53         }
54     }
55 }

 

SignalR需要註意事項

你不會實例化 Hub 類或從伺服器上自己的代碼調用其方法;SignalR Hubs 管道為你完成的所有操作。 SignalR 每次需要處理中心操作(例如客戶端連接、斷開連接或向伺服器發出方法調用時)時,SignalR 都會創建 Hub 類的新實例。

由於 Hub 類的實例是暫時性的,因此無法使用它們來維護從一個方法調用到下一個方法的狀態。 每當伺服器從客戶端收到方法調用時,中心類的新實例都會處理消息。 若要通過多個連接和方法調用來維護狀態,請使用一些其他方法(例如資料庫)或 Hub 類上的靜態變數,或者不派生自 Hub的其他類。 如果在記憶體中保留數據,請使用 Hub 類上的靜態變數等方法,則應用域回收時數據將丟失。

如果要從在 Hub 類外部運行的代碼將消息發送到客戶端,則無法通過實例化 Hub 類實例來執行此操作,但可以通過獲取對 Hub 類的 SignalR 上下文對象的引用來執行此操作。

註意:ChatHub每次調用都是一個新的實例,所以不可以有私有屬性或變數,不可以保存對像的值,所以如果需要記錄一些持久保存的值,則可以採用靜態變數,或者中心以外的對象。

關於源碼

本示例中相關源碼,已上傳至gitee(碼雲),鏈接如下:
https://gitee.com/ahsiang/demo-signal-r

 

 

備註

以上就是WPF+ASP.NET SignalR實現動態折線圖的全部內容,關於SignalR的應用,這隻是一個簡單的入門示例,希望可以拋磚引玉,一起學習,共同進步。學習編程,從關註【老碼識途】開始!!!


作者:小六公子
出處:http://www.cnblogs.com/hsiang/
本文版權歸作者和博客園共有,寫文不易,支持原創,歡迎轉載【點贊】,轉載請保留此段聲明,且在文章頁面明顯位置給出原文連接,謝謝。
關註個人公眾號,定時同步更新技術及職場文章


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JZ76 刪除鏈表中重覆的結點 題目 給定一個二叉樹,返回該二叉樹的之字形層序遍歷,(第一層從左向右,下一層從右向左,一直這樣交替) 例如,給定的二叉樹是{1,2,3,#,#,4,5} 該二叉樹之字形層序遍歷的結果是 [ [1], [3,2], [4,5] ] 方法 非遞歸層次遍歷 思路 演算法實現 ...
  • 核心配置文件 mybatis-config.xml 系統核心配置文件 MyBatis 的配置文件包含了會深深影響 MyBatis 行為的設置和屬性信息。 能配置的內容如下: configuration(配置) properties(屬性) settings(設置) typeAliases(類型別名) ...
  • 一、重用父類方法 1 與繼承沒有關係的重用 指名道姓的使用 在子類里想用父類的方法,我們可以直接用父類名.方法名() >父類里方法有幾個參數就傳幾個參數 我們看起來是子類在調用父類的方法,但是實際上,這並沒有存在繼承關係 class A: def __init__(self,name,age): s ...
  • 1、MyBatis簡介 1.1、什麼是MyBatis MyBatis 是一款優秀的持久層框架 MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集的過程 MyBatis 可以使用簡單的 XML 或註解來配置和映射原生信息,將介面和 Java 的 實體類 【Plain Old ...
  • 最近網上看到了電子郵箱的新利用方法如題,下載了幾個此類軟體,發現好幾個不是不好用,就是功能不全。上博客園搜了一下,那麼可以看到有使用java和python實現的,這裡我們用Windows的批處理實現。 我們要實現的最基礎的功能,自然是執行cmd命令,有了這個其他都好說。 Windows批處理的優點: ...
  • 在 C++ 中為了操作簡潔引入了函數模板。所謂的函數模板實際上是建立一個通用函數,其函數類型或形參類型不具體指定,用一個虛擬的類型來表達,這個通用函數就稱為函數模板。 1、通用的寫法 函數模板不是一個具體的函數,編譯器不能為其生成可執行代碼。定義函數模板後只是一個對函數功能框架的描述,當它具體執行時 ...
  • 力扣104 求二叉樹的最大深度 題目: 給定一個二叉樹,找出其最大深度。 二叉樹的深度為根節點到最遠葉子節點的最長路徑上的節點數。 說明: 葉子節點是指沒有子節點的節點。 示例 給定二叉樹 [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7 返回它的最大深度 3 ...
  • 前言: 前面的四個章節我們主要講解了MongoDB的相關基礎知識,接下來我們就開始進入使用.NET7操作MongoDB開發一個ToDoList系統實戰教程。本章節主要介紹的是如何快熟搭建一個簡單明瞭的後端項目框架。 MongoDB從入門到實戰的相關教程 MongoDB從入門到實戰之MongoDB簡介 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...