Return before async Task complete

12,104

Running tasks in ASP.NET without a request is not recommended. It's dangerous.

That said, change CleanSessions to return Task and you can do it like this:

Task.Run(() => CleanSessions());

In your case, I think it would be OK, because there's no long-term problem if CleanSessions doesn't execute or gets terminated in the middle of executing (which can happen in ASP.NET due to recycling). If you want to notify ASP.NET that you have some work in progress that is not associated with a request, you can use the BackgroundTaskManager from my blog like this:

BackgroundTaskManager.Run(() => CleanSessions());
Share:
12,104
user1084447
Author by

user1084447

Updated on July 18, 2022

Comments

  • user1084447
    user1084447 almost 2 years

    I'm working on an ASP.NET MVC 4 web application. I'm using .NET 4.5 and am trying to take advantage of the new asynchronous API's.

    I have a couple situations where I want to schedule an async Task to run later while I return back an immediately important value right away. For example, here is a "Login" method which I want to return a new SessionID as quickly as possible, but once I've returned the SessionID I want to clean out old expired SessionID's:

    public async Task<Guid> LogIn(string UserName, string Password)
    {
        //Asynchronously get ClientID from DB using UserName and Password
    
        Session NewSession = new Session()
        {
            ClientID = ClientID,
            TimeStamp = DateTime.Now
        };
        DB.Sessions.Add(NewSession);
        await DB.SaveChangesAsync();    //NewSession.ID is autopopulated by DB
    
        CleanSessions(ClientID);    //Async method which I want to execute later
    
        return NewSession.ID;
    }
    
    private async void CleanSessions(int ClientID)
    {
        //Asynchronously get expired sessions from DB based on ClientID and mark them for removal
        await DB.SaveChangesAsync();
    }
    

    I've tried a bunch of different things including combinations of Task.Run() and Parallel.Invoke() but CleanSessions never gets called. How do I achieve background task scheduling?

  • Stephen Cleary
    Stephen Cleary about 11 years
    This solution has the best error handling (using await on CleanSessions), but doesn't return the response "early"; the response will be sent after CleanSessions finishes.
  • Jon Skeet
    Jon Skeet about 11 years
    Ah, yes - even though this code has used ConfigureAwait, the response clearly can't be sent until those "awaits" have finished. So if you imagine the calling code awaiting this task, it'll be in the ASP.NET sync context.
  • user1084447
    user1084447 about 11 years
    Yeah I gave this a try, it still awaits CleanSessions() before it continues. It does not accomplish what I am trying to do.
  • user1084447
    user1084447 about 11 years
    This is exactly what I was looking for, thank you. I actually had already tried this but there was a bug in my CleanSessions() method which was causing an exception, and because the method is being executed without a request the exception wasn't being caught. This was giving me the impression that the method wasn't getting called at all! So a note to anybody looking to use this trick, make sure to read the links in this answer and make sure you really understand why this is "dangerous".
  • CularBytes
    CularBytes about 6 years
    How about 4 years later? Perhaps an update? Your blog has not been updated since 2014, any new insights?
  • Stephen Cleary
    Stephen Cleary about 6 years
    @CularBytes: No; ASP.NET still works the same way, and this answer is still perfectly valid. BackgroundTaskManager won't work on ASP.NET Core, but you can do the same thing using IHostedService or using IApplicationLifetime instead of IRegisteredObject.
  • CularBytes
    CularBytes about 6 years
    Thanks for the update. Eventually just moved it to WebJobs calling an API endpoint and doing the work later (scheduled)