我需要在 asp.net mvc core 应用程序中每天运行一次定时任务。 我该如何实现呢?
谢谢
我需要在 asp.net mvc core 应用程序中每天运行一次定时任务。 我该如何实现呢?
谢谢
2022年11月更新答案
DotNet Core 2推出了一种新的接口IHostedService
,可用于处理后台任务。在这个微软文档中,您可以找到有关在ASP.NET Core中使用托管服务实现后台任务的详细信息:Background tasks with hosted services in ASP.NET Core。
一个简单的HostedService实现:
public class SimpleHostedService : IHostedService
{
private readonly ILogger<SimpleHostedService> _logger;
public SimpleHostedService(ILogger<SimpleHostedService> logger)
{
_logger = logger;
}
public async Task StartAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Timed Hosted Service Started.");
while (!stoppingToken.IsCancellationRequested)
{
DoWork();
// Wait one second
await Task.Delay(1000);
}
}
private void DoWork()
{
// Do something
}
public Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Hosted Service is stopping.");
return Task.CompletedTask;
}
}
关于计划任务,我将展示两个选项:
System.Threading.Timer
来触发任务的DoWork
方法。请查看定时后台任务:public class TimedHostedService : IHostedService, IDisposable
{
private int executionCount = 0;
private readonly ILogger<TimedHostedService> _logger;
private Timer? _timer = null;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Timed Hosted Service running.");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void DoWork(object? state)
{
var count = Interlocked.Increment(ref executionCount);
_logger.LogInformation(
"Timed Hosted Service is working. Count: {Count}", count);
}
public Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Timed Hosted Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
Cron
项目 ServiceWorkerCronJob ,实现更完整和灵活的特定计划配置。 这篇文章 展示了该组件的强大功能以及如何实现。我的建议和鼓励
将所有的 BackgroudServices
放在一个专门的 WorkerService
应用程序中,可以查看一下 .NET 的 Worker Services。
在同一个项目中同时实现 BackgroundServices
和 AspNet Web/Api
(我指的是)会带来很多复杂性,当您需要进行 Scale Out 时更是如此。因此,请记住:
你不能。
你需要使用第三方库,例如 Quarz、Hangfire 或 Azure WebJobs,它们可以从 ASP.NET Core 应用程序内部或外部触发。
但请注意,如果使用 Quarz 或 Hangfire 在 ASP.NET Core 应用程序内部运行它,它可能会受到进程生命周期的影响,即如果在 IIS 或 Azure 应用服务上运行它,则你无法控制 IIS 何时停止应用程序(由于不活动或其他原因)或是否在没有外部请求的情况下启动它(IIS 可以配置为立即重新启动应用程序,缺省设置是在下一次请求时)。
也就是说,当 IIS 或其他进程关闭应用程序时,最好跳过触发器。因此,最好让计划程序在 ASP.NET Core 进程之外运行(例如,在控制台应用程序中、在 Azure 上的后台工作程序中或使用 Azure Web Jobs)。
host.Run()
之前将一个使用 C# 的 System.TaskScheduler 放入 Program.cs 中。 - Ole K这是一个老问题,但我看到了最新框架版本的很多改进,却没有太多关于如何以清晰的方式使其正常工作的信息。
AspNet Core 3.x以比以往更抽象的方式实现了Web主机生成器,允许完美运行不同类型的服务。Web容器的初始化与运行SignalR服务器、Web Api、MVC或工作线程的方式相同,因此我们可以将它们混合使用而没有问题。
例如,以下方式初始化API/MVC应用:
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
我们可以使用模板在通常的启动代码中声明任何类型的服务。按照您需要的方式配置服务,它们将为任何托管的应用程序准备就绪。现在,让我们添加一个定时工作程序,我们需要创建一个简单的类,遵循Microsoft的文档:
internal class TimedHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("Timed Background Service is working.");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
来源:Microsoft
在 Program.cs 文件中将我们的 worker 添加到构建器中:
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<TimedHostedService>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
作为一个使用 System.Threading.Timer 的 Startup.cs 的想法。
// add the below into your Startup contructor
public Startup(IHostingEnvironment env)
{
// [...]
var t = new System.Threading.Timer(doSomething);
// do something every 15 seconds
t.Change(0, 15000);
}
// define a DateTime property to hold the last executed date
public DateTime LastExecuted { get; set; }
// define the doSomething callback in your Startup class
private void doSomething(object state)
{
System.Diagnostics.Debug.WriteLine("The timer callback executes.");
// this callback is called every 15 seconds
// but the condition below makes sure only it
// only takes place at 17:00
// every day, when LastExecuted is not today
if(DateTime.UtcNow.Hour == 17 && DateTime.UtcNow.Minute == 0 && DateTime.UtcNow.Date != LastExecuted.Date)
{
LastExecuted = DateTime.UtcNow;
System.Diagnostics.Debug.WriteLine("#### Do the job");
}
}
DbContext
这样的作用域服务也是如此。如果在其中启动线程,可能会完全破坏ASP.NET管理线程池的方式。 - Tseng$url="http://www.myapp.com/api/runjob"
$content=(New-Object System.Net.WebClient).DownloadString("$url");
$Logfile = "D:\Temp\job.log"
Add-content $Logfile -value $content
2. 将powershell执行策略设置为无限制
打开Powershell并运行以下命令:
Set-Executionpolicy -Scope CurrentUser -ExecutionPolicy UnRestricted
Set-Executionpolicy -Scope Process -ExecutionPolicy UnRestricted
Program: Powershell
Argument: -noprofile -executionpolicy bypass -file "C:\Jobs\myjob.ps1"
https://medium.com/@nickfane/introduction-to-worker-services-in-net-core-3-0-4bb3fc631225
工作服务负责按照给定的时间间隔执行后台任务。
通常,如果您不想使用Azure或任何其他服务,可以创建一个新项目。一种规则引擎。计划任务将存储在数据库中(例如SQL Server),然后应用程序将读取此任务并运行逻辑。因此,通常最好有一个不同的应用程序来处理定期任务(如清理文件、电子邮件等)。这种类型的解决方案使您能够轻松地将逻辑扩展到多种类型的任务并应用规则。例如,根据您决定的各种规则发送电子邮件。