c# - 如何在特定时间触发.NET Core 3.1 Hosted Service?

标签 c# asp.net-core .net-core asp.net-core-hosted-services

我需要在特定时间触发托管服务,例如。每天 13:00 点。我的典型 ExecuteAsync 方法看起来像这样:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
     var interval = _configuration.IntervalInSeconds * 1000;

     while (!stoppingToken.IsCancellationRequested)
     { 
         // some job

         await Task
             .Delay(interval, stoppingToken);
     }
}

当应用程序启动时,会调用 executeAsync 方法,然后每分钟都会执行一些操作。现在我必须只在特定时间执行这样的操作。有什么方法可以实现这种行为吗?除了粗略计算下一次触发时间外,我的问题还有什么解决办法吗??感谢您的帮助。

最佳答案

通过使用 Timer method ,您可以在特定时间运行任务,计算当前时间和目标时间之间的时间跨度,并将其作为 dueTime 参数传递。

代码如下:

    public Task StartAsync(CancellationToken cancellationToken)
    {
        //DateTime.Today: 00:00:00
        TimeSpan delayTime = DateTime.Today.AddHours(21) - DateTime.Now;
        TimeSpan intervalTime = TimeSpan.FromMinutes(1); //86400000s == 1 day
        _timer = new Timer(DoWork, null, delayTime, intervalTime);
        return Task.CompletedTask;
    }

此外,timerTask.Delay() methods更多的应用于指定时间间隔执行一个方法,如果你想实现定时任务,我建议你也可以尝试使用Cronos包和 Cron Expressions配置计划任务(引用:link)。

Cronos 包是一个轻量级但功能完备的库,用于解析 cron 表达式并计算下一次出现时区和夏令时。 Cronos 是 HangfireIO 赞助的开源项目,详细文档可以阅读 its GitHub repository . 详细步骤如下:

  1. 安装 Cronos通过 NuGet 打包。

  2. 使用以下代码创建一个 CronJobService 服务:

     public abstract class CronJobService : IHostedService, IDisposable
     {
         private System.Timers.Timer _timer;
         private readonly CronExpression _expression;
         private readonly TimeZoneInfo _timeZoneInfo;
    
         protected CronJobService(string cronExpression, TimeZoneInfo timeZoneInfo)
         {
             _expression = CronExpression.Parse(cronExpression);
             _timeZoneInfo = timeZoneInfo;
         }
    
         public virtual async Task StartAsync(CancellationToken cancellationToken)
         {
             await ScheduleJob(cancellationToken);
         }
    
         protected virtual async Task ScheduleJob(CancellationToken cancellationToken)
         {
             var next = _expression.GetNextOccurrence(DateTimeOffset.Now, _timeZoneInfo);
             if (next.HasValue)
             {
                 var delay = next.Value - DateTimeOffset.Now;
                 if (delay.TotalMilliseconds <= 0)   // prevent non-positive values from being passed into Timer
                 {
                     await ScheduleJob(cancellationToken);
                 }
                 _timer = new System.Timers.Timer(delay.TotalMilliseconds);
                 _timer.Elapsed += async (sender, args) =>
                 {
                     _timer.Dispose();  // reset and dispose timer
                     _timer = null;
    
                     if (!cancellationToken.IsCancellationRequested)
                     {
                         await DoWork(cancellationToken);
                     }
    
                     if (!cancellationToken.IsCancellationRequested)
                     {
                         await ScheduleJob(cancellationToken);    // reschedule next
                     }
                 };
                 _timer.Start();
             }
             await Task.CompletedTask;
         }
    
         public virtual async Task DoWork(CancellationToken cancellationToken)
         {
             await Task.Delay(5000, cancellationToken);  // do the work
         }
    
         public virtual async Task StopAsync(CancellationToken cancellationToken)
         {
             _timer?.Stop();
             await Task.CompletedTask;
         }
    
         public virtual void Dispose()
         {
             _timer?.Dispose();
         }
     }
    
     public interface IScheduleConfig<T>
     {
         string CronExpression { get; set; }
         TimeZoneInfo TimeZoneInfo { get; set; }
     }
    
     public class ScheduleConfig<T> : IScheduleConfig<T>
     {
         public string CronExpression { get; set; }
         public TimeZoneInfo TimeZoneInfo { get; set; }
     }
    
     public static class ScheduledServiceExtensions
     {
         public static IServiceCollection AddCronJob<T>(this IServiceCollection services, Action<IScheduleConfig<T>> options) where T : CronJobService
         {
             if (options == null)
             {
                 throw new ArgumentNullException(nameof(options), @"Please provide Schedule Configurations.");
             }
             var config = new ScheduleConfig<T>();
             options.Invoke(config);
             if (string.IsNullOrWhiteSpace(config.CronExpression))
             {
                 throw new ArgumentNullException(nameof(ScheduleConfig<T>.CronExpression), @"Empty Cron Expression is not allowed.");
             }
    
             services.AddSingleton<IScheduleConfig<T>>(config);
             services.AddHostedService<T>();
             return services;
         }
     }
    
  3. 创建一个 ScheduleJob.cs:

     public class ScheduleJob: CronJobService
     {
         private readonly ILogger<ScheduleJob> _logger;
    
         public ScheduleJob(IScheduleConfig<ScheduleJob> config, ILogger<ScheduleJob> logger)
             : base(config.CronExpression, config.TimeZoneInfo)
         {
             _logger = logger;
         }
    
         public override Task StartAsync(CancellationToken cancellationToken)
         {
             _logger.LogInformation("ScheduleJob starts.");
             return base.StartAsync(cancellationToken);
         }
    
         public override Task DoWork(CancellationToken cancellationToken)
         {
             _logger.LogInformation($"{DateTime.Now:hh:mm:ss} ScheduleJob is working.");
             return Task.CompletedTask;
         }
    
         public override Task StopAsync(CancellationToken cancellationToken)
         {
             _logger.LogInformation("ScheduleJob is stopping.");
             return base.StopAsync(cancellationToken);
         }
     }
    
  4. 在 ConfigureServices 方法中注册 ScheduleJob 服务。

     public void ConfigureServices(IServiceCollection services)
     {
         services.AddHostedService<HelloWorldHostedService>(); 
    
         services.AddCronJob<ScheduleJob>(c=>
         {
             c.TimeZoneInfo = TimeZoneInfo.Local;
             c.CronExpression = @"25 21 * * *"; // 21:25 PM daily.
         });
    
         services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
     }
    

    然后结果如下:

    enter image description here

关于c# - 如何在特定时间触发.NET Core 3.1 Hosted Service?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64005363/

相关文章:

c# - 采用接口(interface)+抽象类的继承设计。好的做法?

c# - "is"运算符只是 "IsInstanceOfType"方法的语法糖

c# - .NET 字典中的元素是连续的吗?

c# - NPOI HSSF 与 SS 命名空间

.Net 核心支持查询 Vertica

c# - 在 Windows 服务中托管 ASP.NET 5 Web 应用程序

.net - 如何禁用自动 .NET Core 包还原?

.net-core - 我如何告诉 Swashbuckle 5 在 dotnetcore 3.0 中需要主体内容?

c# - 无法执行 Cookie 身份验证 : SignInAsync and AuthenticateAsync not successful

.net-core - .NET Core 中的 Unity 接口(interface)拦截器