Aspnetcore: Recurring tasks

Created on 8 May 2016  路  32Comments  路  Source: dotnet/aspnetcore

I would like to know if there is any best practice for implementing recurring tasks in ASP.NET Core in a cross-platform way. It seems reasonable to outsource long-running tasks to separate apps but I'm especially interested in how to trigger these tasks from the ASP.NET app. Of course I could use the established task scheduler solutions of the various target systems but for simple things it would be much easier if I could simply configure the timing in my app and directly trigger the external apps.

Scott Hanselman wrote this very useful blog post (based on Phil Haacks post) pointing out risks regarding the old ASP.NET
http://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx
Some of the risks will certainly continue to exist, at least the issues that have to do with IIS.

David Fowler mentioned the use of IApplicationLifetime in #1169 to find out when the app wants to shut down.

Remains the question how to execute some code at a certain point in time in my app to trigger a task as an external process. I found code using the TPL, f.e.
Task.Delay(TimeSpan.FromMinutes(countdown)).ContinueWith ...

ContinueWith would then simply start the external app and calculate the next execution time. In that case, I guess, it wouldn't be necessary to use IApplicationLifetime.

I would be very grateful to hear some opinions on this.

Most helpful comment

Is there any 'best practice' for running a background task in ASP.Net Core? Let say you want to respond to the request immediately and log request details to disk. Let's say that going outside of memory incurs an unacceptable latency for the end-user response time. Is there any native way to achieve this? I do this in ASP.Net with HostingEnvironment.QueueBackgroundWorkItem but given that its not available the only thing I can think that might achieve the same result is using in-memory cache with a short expiration and having the callback do the background task.

All 32 comments

There's no ASP.NET specific way to handle this but here are some questions:

  • Does your background task need to touch the http context?
  • Does it need to be durable? What happens if it fails randomly?

In my specific case the background task (external console app) is querying data from several services. It should itself use the Web Api of my app to pass the data it got from the other services. My app would then store these data in the database. I thougth it was a good idea that only my web app has direct access to the database and all dependent console apps would only talk to the API. The external app has its own log file for error handling, at least that is my first approach.

You might want to take a look at Azure WebJobs. They are specifically designed for exactly what you describe https://azure.microsoft.com/en-us/documentation/articles/web-sites-create-web-jobs/

@victorhurdugaci That looks very interesting indeed. Unfortunately for the first test versions of our app we would like to stick with IIS or self-hosting.

You can run the .NET WebJobs SDK in a console or you could even host in directly in the IIS proc. I've never tried the later approach but it could work.

The only disadvantage is that it only supports the full .NET framework, not .NET Core

@victorhurdugaci That sounds really great. Too sad, that one key argument for us to use the new ASP.NET was the possibility to run cross-platform. But I will definitely keep the WebJobs in mind for our coming Windows-only projects.
Maybe it's possible to conditionally compile our app against WebJobs on Windows and a comparable solution on Linux/Mac. Do you think, in general, it's possible to conditionally link to platform specific packages/libraries?

That's not possible today, you'd need two different projects.

Do we have HostingEnvironment.RegisterObject(or HostingEnvironment.QueueBackgroundWorkItem) or any alternate?

@imranbaloch there's no need for RegisterObject as you can just use IApplicationLifetime to track shutdown of the appdomain https://github.com/aspnet/Home/issues/1169.

@davidfowl thanks

@davidfowl

I'm not yet familiar with Kestrel, so here's a novice question: is there any shutdown due to idleness mechanism?

I'm not yet familiar with Kestrel, so here's a novice question: is there any shutdown due to idleness mechanism?

No. Kestrel is just a web server. It literally just parses bytes and turns those bytes into an http context, calls through the ASP.NET hosting layer (which is knows nothing about) and turns responses back into bytes and sends them over the socket.

There is no process management, it runs in your process. It's not an IIS replacement. It's a very low level component. Nobody will kill your process if you're using kestrel directly. That's on you to figure out.

That said, when running on IIS, the same app pool settings apply to your .NET process.

Is there any 'best practice' for running a background task in ASP.Net Core? Let say you want to respond to the request immediately and log request details to disk. Let's say that going outside of memory incurs an unacceptable latency for the end-user response time. Is there any native way to achieve this? I do this in ASP.Net with HostingEnvironment.QueueBackgroundWorkItem but given that its not available the only thing I can think that might achieve the same result is using in-memory cache with a short expiration and having the callback do the background task.

@nicolasr75

If I'm not too late, here's how to do .NET Core console webjobs running with an ASP.NET Core WebApp/API.

The magic's in the publish and the app_data folder structure.

Let me know if I can help.

@MaximRouiller
thanks for the great post! Do I correctly understand that this only runs when hosted on Azure? My current plan is to run my app with Kestrel because it must be installable in closed intranets.

@nicolasr75 Well... web jobs is an Azure only concept so... yeah?

Unless you run Azure Stack... then I don't know. 馃槢

I also wonder how to schedule jobs (everything in-process in my case) that use the correct ASP.NET Core thread pool threads.

@shaneconnolly That might work if you don't mind that apparently a web request is needed for cache entry expiration. #259

I am wondering if I can find out from everyone just how bad the following approach would be? Let's start from the fact that an ASP.NET CORE application is basically a console program. Suppose then that I add the following lines to Main just before code that initializes the webhost

       public static void Main(string[] args)
       {
            ThreadStart threadStart = new ThreadStart(MyBackgroundProcess);
            if (threadStart != null)
            {
                Thread t = new Thread(threadStart);
                t.IsBackground = true;
                t.Start();
            }

So now I have a background thread which in theory can run a process on it. It's true that this process is outside the http context, which won't work for everyone. But can we enumerate the issues that arise here?
Certainly one might be that the process can go down. But what else is problematic? Web farm problems?

@billmcknight1953

Every issue present in this blog post will be present.

How will you deal with changing configuration? Multiple instances running on a single machine? Multiple instances running on multiple machines? How do you handle error handling? If an unhandled exception makes it through the thread, it could take down your app.

Also, if you are running this on Azure and you don't have the "Always On" flag turned on, this task will stop running.

It depends on what you are trying to achieve really.

I'm wondering the same thing as @shaneconnolly.

Let say you want to respond to the request immediately and log request details to disk. Let's say that going outside of memory incurs an unacceptable latency for the end-user response time. Is there any native way to achieve this?

In my case, I want to return a response to a request, and also perform some logging. I don't want to block the response waiting for the logging to complete, instead I want the response to be immediately returned, and the log call to be performed in the background. What's the best approach for this? Should I just schedule a task in the thread pool? I don't mind if log entries are dropped when restarting the server.

Some loggers have write-behind buffering built in to avoid this kind of delay. Check your logger settings.

For example the Console logger has a write queue because console output is slow.

I'm actually just writing a record to a database. The question then becomes "how would I implement this buffering", I assume it'd still need some sort of background task to handle the buffered entries.

@Tratcher That looks pretty useful! I wish it was included as an optional part of the framework rather than having to copy and paste the code into my app though.

If it works well then it may become part of the framework or a community package. Let us know.

Thank you. I haven't tested on my production site yet, but it works great in dev 馃槂 I'm still using Mono in production (having some issues with .NET Core on Debian) so hopefully I don't have any issues there.

@Tratcher - In your BackgroundTaskService example, how would you handle injecting/resolving dependencies with ~transient~ scoped lifetimes, such as Entity Framework repositories? Normally these are instantiated per web request, or singletons in a console app, but I'm not sure how well it'd work in this case where it's running in a website but there's no web request backing it.

Edited: Changed "transient" to "scoped"

Transient services are harmless, but scoped services are harder.
Look at https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro where it calls CreateScope.
You would inject an IServiceProvider, call CreateScope, and then call GetRequiredService to resolve your scoped services. At some point you should dispose that scope, likely when your service is stopped / disposed.

Oops, sorry, I meant "scoped" not "transient". Thanks for the info!

I modified the BackgroundTaskService to pass the scoped IServiceProvider to the work item (ie. modified the work item signature from Func<CancellationToken, Task> to Func<IServiceProvider, CancellationToken, Task>). Seems to work fine.

Closing because there doesn't appear to be any further action required on this issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

farhadibehnam picture farhadibehnam  路  3Comments

BrennanConroy picture BrennanConroy  路  3Comments

Kevenvz picture Kevenvz  路  3Comments

ipinak picture ipinak  路  3Comments

ermithun picture ermithun  路  3Comments