如何在ASP.NET C# Razor Pages实现PostgreSQL数据无刷新实时更新
实现ASP.NET Razor Pages + PostgreSQL实时数据更新(无需页面刷新)
嘿,我明白你现在的困境——PostgreSQL的LISTEN/NOTIFY确实是实现实时更新的核心,但把它和Razor Pages结合起来的完整示例确实不多。下面我会一步步帮你把现有代码改成支持实时更新的版本,用SignalR处理前端实时推送,配合PostgreSQL的通知机制搞定这个需求。
第一步:配置PostgreSQL的NOTIFY触发器
首先得让PostgreSQL在notification表有新数据插入时主动发通知。你可以在数据库里执行以下SQL:
-- 创建发送通知的函数 CREATE OR REPLACE FUNCTION notify_new_notification() RETURNS TRIGGER AS $$ BEGIN -- 把新插入的数据转为JSON作为通知内容,方便后端直接使用 PERFORM pg_notify('new_notification', row_to_json(NEW)::text); RETURN NEW; END; $$ LANGUAGE plpgsql; -- 创建触发器,插入新数据时自动触发上面的函数 CREATE TRIGGER trigger_new_notification AFTER INSERT ON notification FOR EACH ROW EXECUTE FUNCTION notify_new_notification();
这样每次有新数据插入notification表,PostgreSQL就会向new_notification频道发送包含新数据的JSON消息。
第二步:给Razor Pages项目添加SignalR
SignalR是ASP.NET里处理实时双向通信的最佳工具,我们用它把PostgreSQL的通知推送到前端。
- 先安装SignalR的NuGet包:
Install-Package Microsoft.AspNetCore.SignalR
- 创建一个SignalR Hub类(比如
NotificationHub.cs):
using Microsoft.AspNetCore.SignalR; public class NotificationHub : Hub { // 这里暂时留空,主要用来管理客户端连接和推送消息 }
- 在
Program.cs里注册SignalR服务并配置端点:
var builder = WebApplication.CreateBuilder(args); // 注册SignalR服务 builder.Services.AddSignalR(); // 保留你原有的其他服务注册代码... var app = builder.Build(); // 保留你原有的中间件配置代码... // 配置SignalR的访问端点 app.MapHub<NotificationHub>("/notificationHub"); app.Run();
第三步:后端监听PostgreSQL通知并推送消息
我们需要一个后台服务持续监听PostgreSQL的通知,收到通知后通过SignalR推送给所有在线客户端。
创建后台服务类PostgresNotificationListener.cs:
using Microsoft.AspNetCore.SignalR; using Npgsql; public class PostgresNotificationListener : BackgroundService { private readonly IHubContext<NotificationHub> _hubContext; private readonly string _connectionString; public PostgresNotificationListener(IHubContext<NotificationHub> hubContext, IConfiguration configuration) { _hubContext = hubContext; // 这里可以用你原有的Database.Connector()获取连接字符串,或者从配置文件读取 _connectionString = configuration.GetConnectionString("PostgresConnection") ?? Database.Database.Connector(); } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { using var conn = new NpgsqlConnection(_connectionString); await conn.OpenAsync(stoppingToken); // 监听我们之前创建的PostgreSQL通知频道 await conn.ExecuteAsync("LISTEN new_notification;", cancellationToken: stoppingToken); // 绑定通知接收事件 conn.Notification += async (sender, e) => { if (!stoppingToken.IsCancellationRequested) { // 把收到的新数据推送给所有连接的客户端 await _hubContext.Clients.All.SendAsync("ReceiveNewNotification", e.Payload, stoppingToken); } }; // 保持连接,直到服务停止 while (!stoppingToken.IsCancellationRequested) { await conn.WaitAsync(stoppingToken); } } }
然后在Program.cs里注册这个后台服务:
builder.Services.AddHostedService<PostgresNotificationListener>();
第四步:修改Razor Page代码实现实时更新
首先调整你的PageModel,把数据查询改成异步方法,方便复用:
public class YourPageModel : PageModel { // 保留你原有的其他代码... public async Task<List<NotificationModel>> ShowNotificationAsync() { var cs = Database.Database.Connector(); List<NotificationModel> not = new List<NotificationModel>(); using var con = new NpgsqlConnection(cs); await con.OpenAsync(); string query = "Select datumnu, bericht FROM notification ORDER BY datumnu DESC OFFSET 1"; using NpgsqlCommand cmd = new NpgsqlCommand(query, con); using (NpgsqlDataReader dr = await cmd.ExecuteReaderAsync()) { while (await dr.ReadAsync()) { not.Add(new NotificationModel { Datenow = ((DateTime)dr["datumnu"]).ToString("yyyy/MM/dd"), Bericht = dr["bericht"].ToString() }); } } return not; } public async Task OnGetAsync() { // 页面初始加载时获取数据 ViewData["Notifications"] = await ShowNotificationAsync(); } }
然后修改HTML页面,添加SignalR的JS逻辑,实现实时更新表格:
<div class="container-fluid"> <p id="output"></p> <div class="col-3 bg-light"> <h5 class="font-italic text-left">Old Messages</h5> <hr> <table id="notificationTable"> @foreach(var n in (List<NotificationModel>)ViewData["Notifications"]) { <tr> <td><img class="img-fluid" src="/Images/Reservation.png"/></td> <td>@Html.DisplayFor(m => n.Datenow)</td> <td>@Html.DisplayFor(m => n.Bericht)</td> </tr> } </table> <hr> </div> </div> <!-- 引入SignalR的JS库 --> <script src="~/lib/microsoft/signalr/dist/browser/signalr.min.js"></script> <script> // 创建SignalR连接实例 const connection = new signalR.HubConnectionBuilder() .withUrl("/notificationHub") .build(); // 接收后端推送的新通知 connection.on("ReceiveNewNotification", function (payload) { // 解析PostgreSQL发来的JSON数据 const newNotification = JSON.parse(payload); // 格式化日期为yyyy/MM/dd格式 const formattedDate = new Date(newNotification.datumnu).toLocaleDateString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit' }).replace(/\//g, '/'); // 往表格最前面插入新行(和你的查询排序逻辑一致) const table = document.getElementById("notificationTable"); const newRow = table.insertRow(0); const cell1 = newRow.insertCell(0); const cell2 = newRow.insertCell(1); const cell3 = newRow.insertCell(2); cell1.innerHTML = '<img class="img-fluid" src="/Images/Reservation.png"/>'; cell2.textContent = formattedDate; cell3.textContent = newNotification.bericht; }); // 启动SignalR连接,失败自动重试 async function startConnection() { try { await connection.start(); console.log("SignalR连接成功"); } catch (err) { console.error(err); setTimeout(startConnection, 5000); } } // 页面加载时启动连接 startConnection(); // 页面关闭时断开连接 window.addEventListener("beforeunload", function () { connection.stop(); }); </script>
一些注意事项
- 确保你的
NotificationModel类属性和PostgreSQL表字段对应,避免解析数据时出错。 - 如果不想推送完整数据,也可以让PostgreSQL只发一个"更新信号",前端收到后调用API拉取最新列表,这种方式更简单但实时性稍弱。
- 后台服务里已经处理了基本的连接重连逻辑,你可以根据需要添加更多异常处理。
内容的提问来源于stack exchange,提问作者anonD
相关产品推荐
相关产品推荐

