I need to run a scheduled task on every new day in asp.net mvc core application. Can I do it, and how?!
thnx
I need to run a scheduled task on every new day in asp.net mvc core application. Can I do it, and how?!
thnx
Updated answer - Nov 2022
With DotNet Core 2 came up a new interface IHostedService
that will take care of background tasks. On this microsoft document you`ll have all the details about Background tasks with hosted services in ASP.NET Core implementation.
A simple HostedService implementation:
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;
}
}
About Schedule Tasks I`ll show two options:
System.Threading.Timer
to trigger the task's DoWork
method. Take a look at Timed background tasks: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
like project ServiceWorkerCronJob. Here you`ll have an article showing great capabilities of the component and how to implement.My Tip and Encouragement
Put all BackgroudServices
in a dedicated WorkerService
application, take a look at Worker Services in .NET.
Implementing BackgroundServices
togheter AspNet Web/Api
(in the same project I mean) will bring many complexities when you need to Scale Out. So, keep in mind that:
You can't.
You need third party libraries, like Quarz, Hangfire or Azure WebJobs which will trigger it from outside or inside the ASP.NET Core application.
Be aware though that if you use Quarz or Hangfire to run it inside the ASP.NET Core application, it may be subject to process life cycle, i.e. if you run it with IIS or Azure App Service, then you have no control when IIS will stop the application (due to inactivity or whatever) or if it will start it w/o an external request (IIS can be configured to restart the app immediately, default is on next request).
That being said, it could be possible that you skip the trigger when the application is being shut down by IIS or some other process. So its best to have the scheduler run outside of ASP.NET Core process (i.e. console application, background worker on Azure or use Azure Web Jobs).
As an idea using the Startup.cs with System.Threading.Timer.
// 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");
}
}
This is an old question but I've seen a lot of improvements with the latest framework versions and not a lot of information out there how to make it work in a clean way.
AspNet Core 3.x implements the webhost builder in a bit more abstract way than ever, allowing to run different services types perfectly. The initialization of a web container is identical to run a SignalR server, Web Api, MVC or workers so we can mix them up without issues.
For example, having an API/MVC app initialized in the following way:
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
We can use the template to declare any kind of service in the usual Startup code. Configure your services in the way you need them and they will be ready for any hosted application. Now, let's add a timed worker, we need to create a simple class following Microsoft's documentation:
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();
}
}
Source: Microsoft
And add our worker to the builder in Program.cs:
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<TimedHostedService>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Your web app will start, run the normal services to accept requests and also, you will get a timed host service. The advantage is you also get Dependency Injection and you can just add to the TimedHostService constructor any interface and it will be injected by the framework transparently.
I would suggest running a powershell script that makes a GET request to any api/endpoint in your application. This way you will facilitate the windows based task scheduler in a .NET Core Application.
1. Create a powershell script and save it on your server as myjob.ps1
$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. Set the powershell execution policy to unrestricted
Open up Powershell and run the following commands:
Set-Executionpolicy -Scope CurrentUser -ExecutionPolicy UnRestricted
Set-Executionpolicy -Scope Process -ExecutionPolicy UnRestricted
Read more about setting up a scheduled task here:https://www.metalogix.com/help/Content%20Matrix%20Console/SharePoint%20Edition/002_HowTo/004_SharePointActions/012_SchedulingPowerShell.htm
3. Create a scheduled job and execute your script
Create a new task i Task Schedulerer. When creating action set it to "Start a program"
Program: Powershell
Argument: -noprofile -executionpolicy bypass -file "C:\Jobs\myjob.ps1"
If you host your code on Azure something like this is possible. With Azure WebJobs you can request a specific url like a cronjob, but you can also run specific pieces of code if you want to. This can be an asp.net core app if you want to. You do need to change some things in the startup project VS creates for you though.
For example let listen to a queue and send an email when something gets queued. Or create a thumbnail from a big image.
Mor info can be found here: https://azure.microsoft.com/en-us/documentation/articles/websites-dotnet-webjobs-sdk-get-started/
You can developer worker service using .NET Core 3.0 onwards. Take a look on it
https://medium.com/@nickfane/introduction-to-worker-services-in-net-core-3-0-4bb3fc631225
Worker service is responsible for executing background tasks as per the given interval time.
Generally if you don't want to use azure or any other service you can create a new project. Some kind of a rule engine. The scheduled task will be stored on an DB (for example SQL Server) and then the application will read this task and run the logic. So generally it's better to have a different a different application to handle scheduled tasks (like cleaning files, emails etc). This type of solution allows you to easilly scale your logic to multiple type of task and apply rules. For example sending email based on various rules you have decided