aspnetcore使用websocket实时更新商品信息

news/2025/9/24 9:32:32/文章来源:https://www.cnblogs.com/mq0036/p/19108596

aspnetcore使用websocket实时更新商品信息

先演示一下效果,再展示代码逻辑。

中间几次调用过程省略。。。

暂时只用到了下面四个项目

1.产品展示页面中第一次通过接口去获取数据库的列表数据

复制代码
/// <summary>
/// 获取指定的商品目录
/// </summary>
/// <param name="pageSize"></param>
/// <param name="pageIndex"></param>
/// <param name="ids"></param>
/// <returns></returns>
[HttpGet]
[Route("items")]
[ProducesResponseType(typeof(PaginatedViewModel<Catalog>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(IEnumerable<ProductDto>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Catalogs([FromQuery] int pageSize = 10, [FromQuery] int pageIndex = 0, string ids = null)
{if (!string.IsNullOrEmpty(ids)){var items = await GetItemByIds(ids);if (!items.Any()){return BadRequest("ids value invalid. Must be comma-separated list of numbers");}return Ok(items);}var totalItems = await _catalogContext.Catalogs.LongCountAsync();var itemsOnPage = await _catalogContext.Catalogs.OrderBy(c => c.Name).Skip(pageSize * pageIndex).Take(pageSize).ToListAsync();var result = itemsOnPage.Select(x => new ProductDto(x.Id.ToString(), x.Name, x.Price.ToString(), x.Stock.ToString(), x.ImgPath));var model = new PaginatedViewModel<ProductDto>(pageIndex, pageSize, totalItems, result);return Ok(model);}
复制代码

2.在前端页面会把当前页面的产品列表id都发送到websocket中去

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function updateAndSendProductIds(ids) {
    productIds = ids;
 
    // Check if the WebSocket is open
    if (socket.readyState === WebSocket.OPEN) {
        // Send the list of product IDs through the WebSocket connection
        socket.send(JSON.stringify(productIds));
    }
}
 
function fetchData() {
    
    const apiUrl = baseUrl + `/Catalog/items?pageSize=${pageSize}&pageIndex=${currentPage}`;
 
    axios.get(apiUrl)
        .then(response => {
            const data = response.data.data;
            displayProducts(baseUrl, data);
 
            const newProductIds = data.map(product => product.Id);
            // Check if the WebSocket is open
            updateAndSendProductIds(newProductIds);
            // 从响应中获取总页数
            const totalPages = Math.ceil(response.data.count / pageSize);
            displayPagination(totalPages);
 
            // 更新当前页数的显示
            const currentPageElement = document.getElementById('currentPage');
            currentPageElement.textContent = `当前页数: ${currentPage + 1} / 总页数: ${totalPages}`;
        })
        .catch(error => {
            console.error('获取数据失败:', error);
        });
}

3.websocket拿到了id数据可以精确的把当前页面的产品都查出来再推送给product.html页面,通过下面的ReceiveAsync方法获取html发送的数据,再通过timer定时器每秒钟Send方法实时的往页面发送获取到的数据,当然这个是不断的去从redis中去查的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
using System.Net.WebSockets;
using System.Threading.Tasks;
using System;
using WsServer.Handler;
using WsServer.Manager;
using StackExchange.Redis;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using Catalogs.Domain.Catalogs;
using Catalogs.Domain.Dtos;
using System.Net.Sockets;
 
namespace WebScoket.Server.Services
{
    /// <summary>
    /// 实时推送产品主要是最新的库存,其他信息也会更新
    /// </summary>
    public class ProductListHandler : WebSocketHandler
    {
        private System.Threading.Timer _timer;
        private readonly IDatabase _redisDb;
        //展示列表推送
        private string productIdsStr;
        public ProductListHandler(WebSocketConnectionManager webSocketConnectionManager,IConfiguration configuration) : base(webSocketConnectionManager)
        {
            ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(configuration["DistributedRedis:ConnectionString"] ?? throw new Exception("$未能获取distributedredis连接字符串"));
            _redisDb = redis.GetDatabase();
            _timer = new System.Threading.Timer(Send, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
        }
        private void Send(object state)
        {
            // 获取当前时间并发送给所有连接的客户端
            if (productIdsStr != null)
            {
                string[] productIds = System.Text.Json.JsonSerializer.Deserialize<string[]>(productIdsStr);
                string hashKeyToRetrieve = "products";
                List<ProductDto> products = new List<ProductDto>();
 
                foreach (var productId in productIds)
                {
                    if(productId == "null") {
                        continue;
                    }
                    string retrievedProductValue = _redisDb.HashGet(hashKeyToRetrieve, productId);
                    if (!string.IsNullOrEmpty(retrievedProductValue))
                    {
                        //反序列化和构造函数冲突,改造了一下Catalog
                        Catalog catalog = System.Text.Json.JsonSerializer.Deserialize<Catalog>(retrievedProductValue);
                        products.Add(new ProductDto(catalog.Id.ToString(), catalog.Name, catalog.Price.ToString(), catalog.Stock.ToString(), catalog.ImgPath));
                    }
                }
                if (products.Count > 0)
                {
                     SendMessageToAllAsync(System.Text.Json.JsonSerializer.Serialize(products)).Wait();
                }
                else
                {
                    SendMessageToAllAsync("NoProduct").Wait();
                }
            }
        }
        public override async Task ReceiveAsync(WebSocket socket, WebSocketReceiveResult result, byte[] buffer)
        {
            //每次页面有刷新就会拿到展示的id列表
            productIdsStr = System.Text.Encoding.UTF8.GetString(buffer, 0, result.Count);
        }
    }
}

4.html页面就可以拿到最新数据再去绑定到页面

?
1
2
3
4
5
6
7
8
9
socket.addEventListener('message', (event) => {
    if (event.data == "NoProduct") {
        clearProductList();
    }
    // Handle the received product data and update the product list
    const productData = JSON.parse(event.data);
    // Update the product list with the received data (call your displayProducts function)
    displayProducts(baseUrl, productData);
});

 

整个流程就这么简单,但是这里需要保持数据库和redis的数据实时同步,否则页面展示的就不是最新的数据就没意义了。

再回到Catalog.Service服务中。

 private async Task DeleteCache(){//await _redisDb.HashDeleteAsync("products",id); //没必要了await _channel.Writer.WriteAsync("delete_catalog_fromredis");}

再做更新、新增、删除等动作的时候就调用一下DeleteCache方法,往后台服务发送一个channel,当后台收到后就做redis删除并且从初始化sqlserver到redis列表同步的操作

复制代码
using System.Reflection;
using System.Threading.Channels;
using Catalogs.Infrastructure.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using StackExchange.Redis;namespace Catalogs.WebApi.BackgroudServices
{/// <summary>/// 记得任何删除了或者购买了产品后需要删除改产品的键/// </summary>public class InitProductListToRedisService : BackgroundService{private readonly IServiceScopeFactory _serviceScopeFactory;private readonly IDatabase _redisDb;private readonly Channel<string> _channel;private readonly ILogger _logger;public InitProductListToRedisService(IServiceScopeFactory serviceScopeFactory, IConfiguration configuration, Channel<string> channel, ILogger<InitProductListToRedisService> logger){_serviceScopeFactory = serviceScopeFactory;ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(configuration["DistributedRedis:ConnectionString"] ?? throw new Exception("$未能获取distributedredis连接字符串"));_redisDb = redis.GetDatabase();_channel = channel;_logger = logger;}protected override async Task ExecuteAsync(CancellationToken stoppingToken){await Init();while (!_channel.Reader.Completion.IsCompleted){var msg = await _channel.Reader.ReadAsync();if(msg == "delete_catalog_fromredis"){await Init();}}}private async Task Init(){using var scope = _serviceScopeFactory.CreateScope();try{CatalogContext _context = scope.ServiceProvider.GetRequiredService<CatalogContext>();string hashKey = "products";var products = await _context.Catalogs.ToListAsync();await _redisDb.KeyDeleteAsync(hashKey);foreach (var product in products){string productField = product.Id.ToString();string productValue = System.Text.Json.JsonSerializer.Serialize(product);_redisDb.HashSet(hashKey, new HashEntry[] { new HashEntry(productField, productValue) });}_logger.LogInformation($"ProductList is over stored in Redis Hash.");           }catch(Exception ex){_logger.LogError($"ProductLis stored in Redis Hash error.");}}}
}
复制代码

这里还有优化的空间可以只针对怕products的hashset的某个id去更新、删除、新增一条数据。

示例代码:

liuzhixin405/efcore-template (github.com)

 

 

2025-09-24 09:30:48【出处】:https://www.cnblogs.com/morec/p/17947739

=======================================================================================

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/915499.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

漏洞挖掘实战:如何定制化模糊测试技术

本文深入探讨如何定制化模糊测试工具syzkaller来挖掘Linux内核漏洞。从基础架构解析到实战技巧,涵盖权限设置、网络接口测试、结果筛选机制以及七种独特漏洞发现方法,适合安全研究人员参考。适配模糊测试以挖掘漏洞 …

css-遮罩层效果

<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">&…

nuxt3中使用pdfjs-dist实现pdf转换canvas实现浏览

获取 pdfjsLib.GlobalWorkerOptions.workerSrc 的cdn链接地址https://cdnjs.com/libraries/pdf.js 代码 https://files.cnblogs.com/files/li-sir/cspdf.zip?t=1758676920&download=true

查看linux部署网站的TLS版本号

curl https://域名 -version无可奈何花落去,似曾相识燕归来

【SpringBoot- Spring】学习

Spring官方文档翻译(1~6章 转载至 http://blog.csdn.net/tangtong1/article/details/51326887 Spring官方文档、参考中文文档 一、Spring框架概述 Spring框架是一个轻量级的解决方案,可以一站式地构建企业级应用。Sp…

css-更改鼠标样式

<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">&…

css-浮动围绕文字效果

<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">&…

浙江省建设厅网站地址网页游戏排行榜 2020

文章目录 前言不使用对象池使用官方内置对象池应用 自制对象池总结源码参考完结 前言 对象池&#xff08;Object Pool&#xff09;是一种软件设计模式&#xff0c;用于管理和重用已创建的对象。在对象池中&#xff0c;一组预先创建的对象被维护在一个池中&#xff0c;并在需要时…

怎样建设电影网站找人做一个小网站需要多少钱

当下&#xff0c;新媒体矩阵营销已成为众多企业的营销选择之一&#xff0c;各企业可以通过新媒体矩阵实现扩大品牌声量、维持用户关系、提高销售业绩等不同的目的。 而不同目的的矩阵&#xff0c;它的内容运营模式会稍有差别&#xff0c;评价体系也会大不相同。 企业在运营某类…

网站建设与实训怎么给网站引流

在大型语言模型&#xff08;LLM&#xff09;的世界中&#xff0c;有两个强大的框架用于部署和服务LLM&#xff1a;vLLM 和 Text Generation Interface (TGI)。这两个框架都有各自的优势&#xff0c;适用于不同的使用场景。在这篇博客中&#xff0c;我们将对这两个框架进行详细的…

连江网站建设c 语言网站建设

Zotero有着强大的文献管理功能&#xff0c;之前也对其进行过简要介绍&#xff08;Zotero——一款文献管理工具&#xff09;&#xff0c;而安装一些必要的插件则可以使其如虎添翼&#xff0c;今天一起来探索一下一些实用的插件吧&#xff01;&#xff08;排名不分先后&#xff0…

怎样做访问外国网站才能不卡iis部署网站 错误400

一、axios Axios 是一个基于 promise 网络请求库&#xff0c;作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。 二、配置代理 1. 方法一 在…

按照DDD的方式写的一个.net有关Web项目框架

按照DDD的方式写的一个.net有关Web项目框架理想很丰满,现实往往很残酷。 一种按照ddd的方式,根据业务来把自己需要的模块一个一个写出来,再按照模块把需要的接口一个一个的写出来,堆砌一些中间件,以及解耦的comma…

css-图片文字对齐方式

<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">&…

基于Python+Vue开发的摄影网上预约管理系统源码+运行步骤

项目简介该项目是基于Python+Vue开发的摄影网上预约管理系统(前后端分离),影楼婚纱摄影,这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Python编程技能,同时锻炼他们的项目设计与开发…

【习题答案】《深入理解计算机系统(原书第三版)》

第一章 计算机系统漫游考察Amdahl 定律【练习题 1.1】 假设你是个卡车司机,要将土豆从爱达荷州的 Boise 运送到明尼苏达州的 Minneapolis, 全程 2500 公里。在限速范围内,你估计平均速度为 100 公里/小时,整个行程需…

企业网站推广方案网络营销作业seo技巧与技术

引言 一、性能怪兽-Nginx概念深入浅出 二、Nginx环境搭建 三、Nginx反向代理-负载均衡 四、Nginx动静分离 五、Nginx资源压缩 六、Nginx缓冲区 七、Nginx缓存机制 八、Nginx实现IP黑白名单 九、Nginx跨域配置 十、Nginx防盗链设计 十一、Nginx大文件传输配置 十二、Nginx配置SL…

建设网站公司价格怎么查找一个网站开发时间

在Linux上安装Oracle时&#xff0c;经常会报以下错误&#xff1a;无法使用命令 /usr/X11R6/bin/xdpyinfo 自动检查显示器颜色。请检查是否设置了DISPLA在Linux上安装Oracle时&#xff0c;经常会报以下错误&#xff1a;无法使用命令 /usr/X11R6/bin/xdpyinfo 自动检查显示器颜色…

用什么手机软件做网站成都创软科技的口碑

找问题找了一个多小时&#xff0c;不停调试&#xff0c;还修改文件路径&#xff0c;配置路径&#xff0c;开机关机&#xff0c;最后终于做出来了&#xff0c;得出来了一个结论 我绝对是天才 首先 看这个视频 k14 打包发布_哔哩哔哩_bilibili 不出意外&#xff0c;你绝对会在…

什么公司时候做网站佛山科技网站建设

飞书如何修改名称 点击编辑信息 在这里修改姓名就可以啦