文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-04-02 19:55

关注

在实际业务中,当后台数据发生变化,客户端能够实时的收到通知,而不是由用户主动的进行页面刷新才能查看,这将是一个非常人性化的设计。比如数字化大屏,并没有人工的干预,而是自动的刷新数据,那如何才能实现数据的实时刷新呢?本文以一个简单示例,简述如何通过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<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方法,和服务端发送时保持一致。

运行示例

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

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

客户端运行如下:

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

到此这篇关于WPF+ASP.NET SignalR实现后台通知功能的示例代码的文章就介绍到这了,更多相关WPF后台通知内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯