.NET Core 7 SignalR:如何通过DI实现定时统计更新
问题分析与解决方案
核心问题梳理
- 依赖注入方式错误:私有方法
GetStats中使用[FromServices]无效,ASP.NET Core仅支持构造函数/公共方法参数注入,私有方法无法通过该特性获取服务。 - 异步操作未正确等待:原
TimerManager中直接调用异步Action会导致未捕获的异步异常,且无法保证操作执行完成。 - 重复创建定时器:每个客户端连接时都创建新定时器,导致重复推送相同数据,资源浪费。
重构后的代码实现
1. 修正TimerManager以支持异步操作
public class TimerManager { private Timer _timer; private Func<Task> _asyncAction; public DateTime TimerStarted { get; set; } public bool IsTimerStarted { get; set; } public void PrepareTimer(Func<Task> asyncAction, int delayMS) { _asyncAction = asyncAction; // 使用异步回调处理逻辑 _timer = new Timer(async _ => await ExecuteAsync(), null, 1000, delayMS); TimerStarted = DateTime.Now; IsTimerStarted = true; } private async Task ExecuteAsync() { await _asyncAction(); // 保留原120秒自动停止逻辑,如需永久运行可删除此判断 if ((DateTime.Now - TimerStarted).TotalSeconds <= 120) return; IsTimerStarted = false; _timer.Dispose(); } }
2. 重构StatsHub使用构造函数注入
[Authorize] public class StatsHub : Hub { private readonly IQueueService _queueService; private readonly TimerManager _timerManager; // 通过构造函数注入依赖服务 public StatsHub(IQueueService queueService, TimerManager timerManager) { _queueService = queueService; _timerManager = timerManager; } public override async Task OnConnectedAsync() { // 仅当定时器未启动时初始化,避免重复创建 if (!_timerManager.IsTimerStarted) { _timerManager.PrepareTimer(async () => { var results = await _queueService.GetStats(); await Clients.All.SendAsync("SendQueueStats", results.Payload); }, 15000); } await base.OnConnectedAsync(); } }
3. 配置服务注册(Program.cs)
确保TimerManager注册为单例,保证全局仅一个定时器实例:
builder.Services.AddSingleton<TimerManager>(); builder.Services.AddScoped<IQueueService, QueueService>(); // 替换为你的实现类 builder.Services.AddSignalR();
关键优化说明
- 构造函数注入:通过构造函数获取依赖服务,符合ASP.NET Core DI规范,确保服务生命周期正确管理。
- 异步安全定时器:将原同步
Action改为Func<Task>,并在定时器回调中异步等待操作完成,避免异步异常丢失。 - 全局单例定时器:仅在第一个客户端连接时启动定时器,避免重复推送和资源浪费。
内容的提问来源于stack exchange,提问作者99823
相关产品推荐
相关产品推荐

