You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将ASP.NET SignalR引用迁移至ASP.NET Core Razor Pages

ASP.NET Core Razor Pages 适配SignalR股票播报示例的修改方案

一、服务端核心代码(StockTicker.cs)重构

ASP.NET Core SignalR完全重构了API,需替换命名空间并调整HubContext获取方式:

  1. 替换命名空间
    移除旧的Microsoft.AspNet.SignalR和Microsoft.AspNet.SignalR.Hubs,改用Microsoft.AspNetCore.SignalR和System.Threading(Timer相关)。

  2. 重构单例与依赖注入
    取消原GlobalHost的硬编码实例化,改为通过依赖注入获取IHubContext<StockTickerHub>,并将StockTicker注册为单例服务。

  3. 调整广播逻辑
    原Clients.All.updateStockPrice(stock)改为Core SignalR的异步广播方法_hubContext.Clients.All.SendAsync("updateStockPrice", stock),同时补全Stock对象的Change和ChangePercent计算逻辑。

修改后的完整代码:

using System.Collections.Concurrent;
using System.Threading;
using Microsoft.AspNetCore.SignalR;

namespace SignalR.StockTicker
{
    public class StockTicker
    {
        private readonly IHubContext<StockTickerHub> _hubContext;
        private readonly ConcurrentDictionary<string, Stock> _stocks = new ConcurrentDictionary<string, Stock>();
        private readonly object _updateStockPricesLock = new object();
        private readonly double _rangePercent = .002;
        private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(250);
        private readonly Random _updateOrNotRandom = new Random();
        private readonly Timer _timer;
        private volatile bool _updatingStockPrices = false;

        // 通过依赖注入获取HubContext
        public StockTicker(IHubContext<StockTickerHub> hubContext)
        {
            _hubContext = hubContext;

            _stocks.Clear();
            var stocks = new List<Stock>
            {
                new Stock { Symbol = "MSFT", Price = 30.31m, Open = 30.31m },
                new Stock { Symbol = "AAPL", Price = 578.18m, Open = 578.18m },
                new Stock { Symbol = "GOOG", Price = 570.30m, Open = 570.30m }
            };
            stocks.ForEach(stock => _stocks.TryAdd(stock.Symbol, stock));

            _timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
        }

        public IEnumerable<Stock> GetAllStocks()
        {
            return _stocks.Values;
        }

        private void UpdateStockPrices(object state)
        {
            lock (_updateStockPricesLock)
            {
                if (!_updatingStockPrices)
                {
                    _updatingStockPrices = true;

                    foreach (var stock in _stocks.Values)
                    {
                        if (TryUpdateStockPrice(stock))
                        {
                            BroadcastStockPrice(stock);
                        }
                    }

                    _updatingStockPrices = false;
                }
            }
        }

        private bool TryUpdateStockPrice(Stock stock)
        {
            var r = _updateOrNotRandom.NextDouble();
            if (r > .1)
            {
                return false;
            }

            var random = new Random((int)Math.Floor(stock.Price));
            var percentChange = random.NextDouble() * _rangePercent;
            var pos = random.NextDouble() > .51;
            var change = Math.Round(stock.Price * (decimal)percentChange, 2);
            change = pos ? change : -change;

            var oldPrice = stock.Price;
            stock.Price += change;
            // 补全Change和ChangePercent计算
            stock.Change = stock.Price - stock.Open;
            stock.ChangePercent = stock.Change / stock.Open;
            return true;
        }

        private async void BroadcastStockPrice(Stock stock)
        {
            // 使用Core SignalR异步广播方法
            await _hubContext.Clients.All.SendAsync("updateStockPrice", stock);
        }
    }
}

二、实现StockTickerHub

创建SignalR Hub类,暴露客户端调用的GetAllStocks方法:

using Microsoft.AspNetCore.SignalR;

namespace SignalR.StockTicker
{
    public class StockTickerHub : Hub
    {
        private readonly StockTicker _stockTicker;

        public StockTickerHub(StockTicker stockTicker)
        {
            _stockTicker = stockTicker;
        }

        public IEnumerable<Stock> GetAllStocks()
        {
            return _stockTicker.GetAllStocks();
        }
    }
}

三、完善Program.cs配置

在Program.cs中注册SignalR服务、StockTicker单例,并映射Hub端点:

var builder = WebApplication.CreateBuilder(args);

// 添加Razor Pages和SignalR服务
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
// 注册StockTicker为单例
builder.Services.AddSingleton<StockTicker>();

var app = builder.Build();

// 环境配置
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();

// 映射Razor Pages和SignalR Hub
app.MapRazorPages();
app.MapHub<StockTickerHub>("/stockTickerHub"); // 端点路径需与客户端一致

app.Run();

四、客户端脚本与页面调整

1. 更新页面脚本引用

移除旧的jQuery和ASP.NET SignalR脚本,改用ASP.NET Core SignalR官方客户端库:

<!-- 移除旧脚本 -->
<!-- <script src="/Scripts/jquery-1.10.2.min.js" ></script> -->
<!-- <script src="/Scripts/jquery.signalR-2.1.0.js"></script> -->
<!-- <script src="/signalr/hubs"></script> -->

<!-- 引入ASP.NET Core SignalR客户端 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/7.0.10/signalr.min.js"></script>
<!-- 引用自定义脚本 -->
<script src="/StockTicker.js"></script>

2. 重构StockTicker.js(移除jQuery依赖)

改用原生JavaScript实现客户端逻辑,适配Core SignalR API:

if (!String.prototype.supplant) {
    String.prototype.supplant = function (o) {
        return this.replace(/{([^{}]*)}/g,
            function (a, b) {
                var r = o[b];
                return typeof r === 'string' || typeof r === 'number' ? r : a;
            }
        );
    };
}

document.addEventListener('DOMContentLoaded', function () {
    const up = '▲',
          down = '▼',
          stockTable = document.getElementById('stockTable'),
          stockTableBody = stockTable.querySelector('tbody'),
          rowTemplate = '<tr data-symbol="{Symbol}"><td>{Symbol}</td><td>{Price}</td><td>{Open}</td><td>{Direction} {Change}</td><td>{PercentChange}</td></tr>';

    function formatStock(stock) {
        return {
            ...stock,
            Price: stock.Price.toFixed(2),
            Change: stock.Change.toFixed(2),
            PercentChange: (stock.ChangePercent * 100).toFixed(2) + '%',
            Direction: stock.Change === 0 ? '' : stock.Change >= 0 ? up : down
        };
    }

    // 创建SignalR连接
    const connection = new signalR.HubConnectionBuilder()
        .withUrl("/stockTickerHub") // 与Program.cs中MapHub路径一致
        .build();

    // 接收服务器推送的更新
    connection.on("updateStockPrice", function (stock) {
        const displayStock = formatStock(stock);
        const rowHtml = rowTemplate.supplant(displayStock);
        const existingRow = stockTableBody.querySelector(`tr[data-symbol='${stock.Symbol}']`);
        
        if (existingRow) {
            existingRow.outerHTML = rowHtml;
        } else {
            stockTableBody.insertAdjacentHTML('beforeend', rowHtml);
        }
    });

    // 初始化获取所有股票
    async function init() {
        try {
            const stocks = await connection.invoke("GetAllStocks");
            stockTableBody.innerHTML = '';
            stocks.forEach(stock => {
                const displayStock = formatStock(stock);
                stockTableBody.insertAdjacentHTML('beforeend', rowTemplate.supplant(displayStock));
            });
        } catch (err) {
            console.error(err);
        }
    }

    // 启动连接
    connection.start().then(init).catch(err => console.error(err.toString()));
});

五、页面模型简化(可选)

若页面无需额外业务逻辑,可简化StockTickerModel.cs:

using Microsoft.AspNetCore.Mvc.RazorPages;

namespace SignalR.Pages
{
    public class StockTickerModel : PageModel
    {
        public void OnGet()
        {
        }
    }
}

内容的提问来源于stack exchange,提问作者Douar Gwenn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 17:42:32