WPF+ASP.NET SignalR实现后台通知功能的示例代码

作者:小六公子 时间:2021-10-11 04:49:45 

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

WPF+ASP.NET SignalR实现后台通知功能的示例代码

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

涉及知识点

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

  • 开发工具:Visual Studio 2022 目标框架:.NET6.0

  • ASP.NET SignalR,一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信,目前新版已支持.NET6.0及以上版本。在本示例中,作为消息通知的服务端。

  • WPF,是微软推出的基于Windows 的用户界面框架,主要用于开发客户端程序。

前提条件

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

  • 在服务端有一个常驻内存对象,监听数据变化。

  • 常驻内存对象,可以访问中心对象(ChatHub),能够获取中心对象的所有连接客户端,并发送消息。

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

服务端

经过以上分析后,服务端分为两方面,核心对象(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<ChatHub>)获取连接信息,然后发送消息,如下所示:

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;//自动重新
           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<ChatHub> 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<IHubContext<ChatHub>>();
   Worker.Register(hubContext);//调用静态方法注册

if (next != null)
   {
       await next.Invoke();
   }
});
app.MapControllers();
//2.映射路由
app.UseEndpoints(endpoints => {
   endpoints.MapHub<ChatHub>("/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

/// <summary>
       /// 初始化Connection对象
       /// </summary>
       private void InitInfo()
       {
           hubConnection = new HubConnectionBuilder().WithUrl("https://localhost:7149/chat").WithAutomaticReconnect().Build();
           hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(5);
       }

/// <summary>
       /// 监听
       /// </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;
       }
   }
}

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

运行示例

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

WPF+ASP.NET SignalR实现后台通知功能的示例代码

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

WPF+ASP.NET SignalR实现后台通知功能的示例代码

客户端运行如下:

WPF+ASP.NET SignalR实现后台通知功能的示例代码

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

来源:https://www.cnblogs.com/hsiang/p/16687365.html

标签:WPF,ASP.NET,SignalR,后台,通知
0
投稿

猜你喜欢

  • Unity 从Resources中动态加载Sprite图片的操作

    2023-08-26 11:37:18
  • Kotlin使用静态变量与静态方法详解

    2021-10-11 07:03:40
  • Android开发之文件操作模式深入理解

    2023-10-14 02:48:12
  • java模拟实现斗地主发牌小程序

    2021-07-20 01:55:11
  • Spring如何在一个事务中开启另一个事务

    2021-08-30 17:29:15
  • C#图像处理之图像平移的方法

    2021-12-16 08:38:37
  • 详解StackExchange.Redis通用封装类分享

    2022-07-06 00:41:32
  • C#/VB.NET 自定义PPT动画路径的步骤

    2022-11-25 10:07:49
  • java15新功能的详细讲解

    2023-08-23 04:40:21
  • 简略分析Android的Retrofit应用开发框架源码

    2023-09-17 22:12:30
  • 关于springboot集成阿里云短信的问题

    2023-08-23 09:46:15
  • Java手动实现Redis的LRU缓存机制

    2023-07-31 12:51:30
  • C#事件中关于sender的用法解读

    2022-09-30 20:28:39
  • java中Hashmap的get方法使用

    2023-10-29 13:10:05
  • java字符串遍历的几种常用方法总结

    2022-08-19 06:36:41
  • java 读取本地文件实例详解

    2023-08-12 20:41:32
  • Java中Function的使用及说明

    2023-08-12 03:04:29
  • Spring Aop 如何获取参数名参数值

    2022-09-08 17:00:41
  • C# 文件下载之断点续传实现代码

    2021-05-24 15:37:02
  • 基于多网卡环境下Eureka服务注册IP的选择问题

    2022-09-16 17:59:15
  • asp之家 软件编程 m.aspxhome.com