• WPF+ASP.NET SignalR实现后台通知


    在实际业务中,当后台数据发生变化,客户端能够实时的收到通知,而不是由用户主动的进行页面刷新才能查看,这将是一个非常人性化的设计。比如数字化大屏,并没有人工的干预,而是自动的刷新数据,那如何才能实现数据的实时刷新呢?本文以一个简单示例,简述如何通过WPF+ASP.NET SignalR实现消息后台通知以及数据的实时刷新,仅供学习分享使用,如有不足之处,还请指正。

     通过上一篇文章的学习,了解了如何通过SignalR实现在线聊天功能,在示例中,我们发现每一次的客户端连接都是一个新的实例对象,所以没有办法在中心对象中存储状态信息,所以为了存储用户列表,我们采用了静态变量的方式。并且在线聊天功能是用户发送一条消息(Chat),然后触发中心对象(ChatHub),转发给另一个用户(SendAsync)。那么如果实现数字化大屏,需要服务端持续的往客户端发送消息,而不是客户端主动触发,应该怎么做呢?这就是本文需要分享的内容。

    涉及知识点


    在本示例中,涉及知识点如下所示:

    1. 开发工具:Visual Studio 2022 目标框架:.NET6.0
    2. ASP.NET SignalR,一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信,目前新版已支持.NET6.0及以上版本。在本示例中,作为消息通知的服务端。
    3. WPF,是微软推出的基于Windows 的用户界面框架,主要用于开发客户端程序。

    前提条件


    实现服务端持续往客户端发送消息,除了业务上的需求外,还需要满足两个条件:

    1. 在服务端有一个常驻内存对象,监听数据变化。
    2. 常驻内存对象,可以访问中心对象(ChatHub),能够获取中心对象的所有连接客户端,并发送消息。

    满足以上两个条件,才可以实现想要的功能。

    服务端


    经过以上分析后,服务端分为两方面,核心对象(ChatHub),处理业务对象(Worker)。下面我们逐一说明:

    ChatHub 中心是用于向连接到 SignalR 服务器的客户端发送消息的核心抽象,负责客户端的连接和断开。如下所示:

    1. using Microsoft.AspNetCore.SignalR;
    2. namespace SignalRChat.Chat
    3. {
    4. public class ChatHub:Hub
    5. {
    6. public override Task OnConnectedAsync()
    7. {
    8. Console.WriteLine($"ID:{Context.ConnectionId} 已连接");
    9. return base.OnConnectedAsync();
    10. }
    11. public override Task OnDisconnectedAsync(Exception? exception)
    12. {
    13. Console.WriteLine($"ID:{Context.ConnectionId} 已断开");
    14. return base.OnDisconnectedAsync(exception);
    15. }
    16. }
    17. }

    Worker实例为一个单例对象,常驻内容,实时监听数据变化,并通过ChatHub上下文(IHubContext)获取连接信息,然后发送消息,如下所示:

    1. using Microsoft.AspNetCore.SignalR;
    2. namespace SignalRChat.Chat
    3. {
    4. public class Worker
    5. {
    6. public static Worker Instance;
    7. private static readonly object locker=new object();
    8. private IHubContext context;
    9. private System.Timers.Timer timer;
    10. public Worker(IHubContext context) {
    11. this.context = context;
    12. timer= new System.Timers.Timer(500);//单位毫秒
    13. timer.Enabled=true;
    14. timer.AutoReset=true;//自动重新
    15. timer.Elapsed += Timer_Elapsed;
    16. timer.Start();
    17. }
    18. private void Timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
    19. {
    20. //模拟数据,一般情况下,从数据库获取,然后通知到客户端
    21. Dictionary<string, object> data = new Dictionary<string, object>();
    22. var online = new Random().Next(0, 100);
    23. var male = Math.Floor(new Random().NextSingle() * online);
    24. var female = online - male;
    25. data["online"]=online;
    26. data["male"] =male;
    27. data["female"] = female;
    28. context.Clients.All.SendAsync("Data",data);
    29. }
    30. public static void Register(IHubContext context)
    31. {
    32. if (Instance == null)
    33. {
    34. lock (locker)
    35. {
    36. if (Instance == null)
    37. {
    38. Instance = new Worker(context);
    39. }
    40. }
    41. }
    42. }
    43. }
    44. }

    注意:此处发送数据的是Data方法,客户端必须监听Data方法,才能接收数据。

    如何创建单例对象呢,中心对象上下文不能自己创建,必须要和ChatHub通过注入方式的上下文是同一个,不然无法获取客户端连接信息。在项目启动时,通过中间件的方式创建,如下所示:

    1. using Microsoft.AspNetCore.SignalR;
    2. using SignalRChat.Chat;
    3. var builder = WebApplication.CreateBuilder(args);
    4. // Add services to the container.
    5. builder.Services.AddControllers();
    6. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    7. builder.Services.AddEndpointsApiExplorer();
    8. builder.Services.AddSwaggerGen();
    9. //1.添加SignalR服务
    10. builder.Services.AddSignalR();
    11. var app = builder.Build();
    12. // Configure the HTTP request pipeline.
    13. if (app.Environment.IsDevelopment())
    14. {
    15. app.UseSwagger();
    16. app.UseSwaggerUI();
    17. }
    18. app.UseRouting();
    19. app.UseHttpsRedirection();
    20. app.UseAuthorization();
    21. //在Use中注册单例实例
    22. app.Use(async (context, next) =>
    23. {
    24. var hubContext = context.RequestServices
    25. .GetRequiredService>();
    26. Worker.Register(hubContext);//调用静态方法注册
    27. if (next != null)
    28. {
    29. await next.Invoke();
    30. }
    31. });
    32. app.MapControllers();
    33. //2.映射路由
    34. app.UseEndpoints(endpoints => {
    35. endpoints.MapHub("/chat");
    36. });
    37. app.Run();

    客户端


    客户端主要是连接服务器,然后监听服务端发送数据的方法即可,如下所示:

    1. namespace SignalRClient
    2. {
    3. public class ShowDataViewModel : ObservableObject
    4. {
    5. #region 属性及构造函数
    6. private int online;
    7. public int Online
    8. {
    9. get { return online; }
    10. set { SetProperty(ref online, value); }
    11. }
    12. private int male;
    13. public int Male
    14. {
    15. get { return male; }
    16. set { SetProperty(ref male, value); }
    17. }
    18. private int female;
    19. public int Female
    20. {
    21. get { return female; }
    22. set { SetProperty(ref female, value); }
    23. }
    24. private HubConnection hubConnection;
    25. public ShowDataViewModel()
    26. {
    27. }
    28. #endregion
    29. #region 命令
    30. private ICommand loadedCommand;
    31. public ICommand LoadedCommand
    32. {
    33. get
    34. {
    35. if (loadedCommand == null)
    36. {
    37. loadedCommand = new RelayCommand<object>(Loaded);
    38. }
    39. return loadedCommand;
    40. }
    41. }
    42. private void Loaded(object obj)
    43. {
    44. //1.初始化
    45. InitInfo();
    46. //2.监听
    47. Listen();
    48. //3.连接
    49. Link();
    50. }
    51. #endregion
    52. ///
    53. /// 初始化Connection对象
    54. ///
    55. private void InitInfo()
    56. {
    57. hubConnection = new HubConnectionBuilder().WithUrl("https://localhost:7149/chat").WithAutomaticReconnect().Build();
    58. hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(5);
    59. }
    60. ///
    61. /// 监听
    62. ///
    63. private void Listen()
    64. {
    65. hubConnection.Onstring,object>>("Data", ReceiveInfos);
    66. }
    67. ///
    68. /// 连接
    69. ///
    70. private async void Link()
    71. {
    72. try
    73. {
    74. await hubConnection.StartAsync();
    75. }
    76. catch (Exception ex)
    77. {
    78. MessageBox.Show(ex.Message);
    79. }
    80. }
    81. private void ReceiveInfos(Dictionary<string, object> data)
    82. {
    83. if (data == null || data.Count < 1)
    84. {
    85. return;
    86. }
    87. int.TryParse(data["online"]?.ToString(),out int online);
    88. int.TryParse(data["male"]?.ToString(),out int male);
    89. int.TryParse(data["female"]?.ToString(),out int female);
    90. this.Online=online;
    91. this.Male = male;
    92. this.Female=female;
    93. }
    94. }
    95. }

    注意:监听Data方法,和服务端发送时保持一致。

    运行示例


    在示例中,需要同时启动服务端和客户端,所以以多项目方式启动,如下所示:

     运行成功后,服务端以ASP.NET Web API的方式呈现,如下所示:

     客户端运行如下:

    注意:客户端可以有多个,也可以是一个,后台通知消息,会通知到每一个连接的客户端。

    源码下载


    关注微信公众号,然后发送信息SignalR即可获取下载链接。如下所示:

    备注


    以上就是WPF+ASP.NET SignalR实现后台实时通知的全部内容,关于SignalR的应用,实际场景有很多,这只是一个简单的入门示例,希望可以抛砖引玉,一起学习,共同进步。学习编程,从关注【老码识途】开始!!!

  • 相关阅读:
    不经意传输扩展(OTE)-不经意伪随机函数(OPRF)-隐私集合求交(PSI)
    如何在Kubernetes中使用cert-manager部署SSL
    Spring Cloud Loadbalancer 实现客户端负载均衡
    Veritas Backup Exec v22.2.1193.1605 数据备份恢复软件
    python常用标准库(压缩包模块zipfile和tarfile)
    使用HTML制作静态网站作业——我的校园运动会(HTML+CSS)
    语言模型的发展
    【Java Web】实现帖子点赞功能——基于Redis
    算法通关村之堆结构(实战训练)经典问题:理解堆的构造、插入、删除过程,查找数组中第K大的元素、堆排序、合并K个有序链表
    Promise笔记(一)
  • 原文地址:https://blog.csdn.net/fengershishe/article/details/126844091