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

ASP.NET Core 达到指定时间时如何向用户推送通知

最优实现方案整理

你之前依赖HttpRequest触发查询的方案确实不可靠,一方面没有请求流入时任务完全不会执行,另一方面高频请求下反复查库也会造成不必要的性能损耗,以下是不同场景下的可选实现方案:

方案1:ASP.NET Core 内置后台托管服务(轻量场景首选)

不需要依赖外部中间件,直接和你现有项目集成,根据是否接受第三方依赖分为两种实现:

  • 引入第三方调度库(推荐,省掉大量容错逻辑开发)
    用Hangfire作为调度框架,自带任务持久化、失败重试、分布式锁能力,直接复用你现有的SQL Server作为存储即可,实现步骤如下:

    1. 安装Nuget包:Hangfire.Core、Hangfire.SqlServer、Hangfire.AspNetCore
    2. Program.cs中注册服务:
    builder.Services.AddHangfire(config => config
      .UseSqlServerStorage(builder.Configuration.GetConnectionString("你的SQL Server连接字符串名称")));
    builder.Services.AddHangfireServer();
    // 注册你自己的通知推送服务
    builder.Services.AddScoped<NotificationService>();
    
    1. 实现推送逻辑:
    public class NotificationService
    {
        public async Task SendScheduledNotification(int notificationId)
        {
            // 1. 从数据库查询对应通知内容、接收用户信息
            // 2. 执行推送(APP/短信/邮件等逻辑)
            // 3. 标记该条通知为已发送,避免重复推送
        }
    }
    
    1. 项目启动时批量初始化待执行任务,新增通知时也实时添加调度即可:
    // 项目启动时扫表,添加所有未到时间的待推送任务
    var pendingNotifications = await dbContext.Notifications
        .Where(n => !n.IsSent && n.ScheduledTime > DateTime.Now)
        .ToListAsync();
    foreach (var item in pendingNotifications)
    {
        // 指定任务执行时间为你表中存储的推送时间
        BackgroundJob.Schedule<NotificationService>(
            s => s.SendScheduledNotification(item.Id),
            item.ScheduledTime
        );
    }
    

    注:多实例分布式部署场景下,Hangfire的SQL Server存储自带分布式锁,不会出现多实例重复执行同个推送任务的问题,还自带管控面板可查看任务执行状态。

  • 完全原生实现(不引入第三方依赖)
    用ASP.NET Core自带的BackgroundService做常驻后台服务,定期轮询数据库拉取待推送任务,降低查库频率:

    public class NotificationBackgroundService : BackgroundService
    {
        private readonly IServiceScopeFactory _scopeFactory;
        // 轮询间隔可根据你的业务精度要求调整,建议不低于1分钟
        private readonly TimeSpan _pollInterval = TimeSpan.FromMinutes(5);
    
        public NotificationBackgroundService(IServiceScopeFactory scopeFactory)
        {
            _scopeFactory = scopeFactory;
        }
    
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                using var scope = _scopeFactory.CreateScope();
                var dbContext = scope.ServiceProvider.GetRequiredService<你的DbContext类>();
                // 拉取接下来一个轮询周期内需要推送的通知
                var upcomingNotifications = await dbContext.Notifications
                    .Where(n => !n.IsSent && n.ScheduledTime <= DateTime.Now.Add(_pollInterval) && n.ScheduledTime > DateTime.Now)
                    .ToListAsync(stoppingToken);
                
                foreach (var notification in upcomingNotifications)
                {
                    var delay = notification.ScheduledTime - DateTime.Now;
                    if (delay > TimeSpan.Zero)
                    {
                        // 后台延迟到指定时间执行推送
                        _ = Task.Run(async () =>
                        {
                            await Task.Delay(delay, stoppingToken);
                            // 执行推送逻辑,标记为已发送
                        }, stoppingToken);
                    }
                }
                // 等待下一个轮询周期
                await Task.Delay(_pollInterval, stoppingToken);
            }
        }
    }
    

    注册服务即可生效:

    builder.Services.AddHostedService<NotificationBackgroundService>();
    

方案2:独立调度服务(中大规模场景)

如果你的推送量很大,或者不想让调度逻辑和Web服务耦合,单独部署一个专用的调度服务即可,调度框架还是可以用Hangfire或者Quartz.NET,和Web服务共用同一个SQL Server数据库即可,优势是Web服务的重启、扩缩容完全不会影响调度任务的稳定性。


内容的提问来源于stack exchange,提问作者mohamad rahimi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 23:45:00