Marius Schulz
Marius Schulz
Front End Engineer

Using QueueBackgroundWorkItem to Schedule Background Jobs from an ASP.NET Application in .NET 4.5.2

Starting with the recently released version 4.5.2 of the .NET Framework, ASP.NET now supports the HostingEnvironment.QueueBackgroundWorkItem method found in the System.Web.Hosting namespace. I want to quickly show you how you can use it to schedule background work items in an ASP.NET MVC application.

The HostingEnvironment.QueueBackgroundWorkItem Method in Action

#What Does QueueBackgroundWorkItem Do?

In the release notes, the QueueBackgroundWorkItem method is summarized as follows:

The HostingEnvironment.QueueBackgroundWorkItem method lets you schedule small background work items. ASP.NET tracks these items and prevents IIS from abruptly terminating the worker process until all background work items have completed.

The summary (emphasis mine) highlights the reason for using QueueBackgroundWorkItem: You won't have to worry about processes being shut down prematurely by IIS.

Note that QueueBackgroundWorkItem can only be called inside an ASP.NET managed app domain. It won't work if the runtime host is either Internet Explorer or some Windows shell. For more information on app domains, please refer to Using Application Domains.

#Basic Usage of the QueueBackgroundWorkItem Method

The QueueBackgroundWorkItem method defines two overloads, each of which accepts a single parameter. You can pass either of the following delegate types:

  • Action<CancellationToken>
  • Func<CancellationToken, Task>

Here's how you could pass a lambda expression to the first overload:

HostingEnvironment.QueueBackgroundWorkItem(cancellationToken =>
{
    // Some long-running job
});

The lambda can even be async so that you can use all the goodness await has to offer:

HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
{
    var result = await LongRunningMethodAsync();

    // Do something with result
    // ...
});

Passing a method group to the overload accepting a Func is possible as well:

private void QueueWorkItem()
{
    Func<CancellationToken, Task> workItem = LongRunningMethodAsync;
    HostingEnvironment.QueueBackgroundWorkItem(workItem);
}

private async Task LongRunningMethodAsync(CancellationToken cancellationToken)
{
    // Some long-running job
}

Due to the way that the C# compiler does method group conversion, it's not possible to pass LongRunningMethodAsync directly to QueueBackgroundWorkItem. The issue is that the compiler utilizes overload resolution when converting the method group, and overload resolution doesn't take into account return types.

Since both Action and Func overloads accept a single parameter of type CancellationToken, there's no way to distinguish the two method calls just by looking at their parameter types. The assignment to the workItem variable provides the compiler with that missing type information. For more detail, make sure to read this great StackOverflow answer from Eric Lippert Himself™.

#Posting to a Remote API from an ASP.NET MVC Controller

Here's a more complete example of how QueueBackgroundWorkItem can be used in an ASP.NET MVC controller. After creating some Foo model, the controller registers a background work item which makes a (potentially) long-running call to a remote API:

public class FooController : Controller
{
    [HttpPost]
    public ActionResult Create(FooInputModel input)
    {
        // Process the input somehow
        // ...

        Action<CancellationToken> workItem = PostToRemoteService;
        HostingEnvironment.QueueBackgroundWorkItem(workItem);

        return View();
    }

    private async void PostToRemoteService(CancellationToken cancellationToken)
    {
        using (var client = new HttpClient())
        {
            var response = await client.PostAsync("https://example.com/endpoint",
                new StringContent("..."), cancellationToken);

            // Do something with response
            // ...
        }
    }

    // More action methods
    // ...
}

That way, the controller can return an ActionResult (in this case, a view) after the input has been processed. It doesn't have to wait until the HTTP request to the remote API has been made and a response is returned.

Of course, you can call the QueueBackgroundWorkItem method from other ASP.NET application types as well, it is in no way specific to MVC.

#Summary

As you've seen, the new QueueBackgroundWorkItem method is very easy to use with different delegate parameters. ASP.NET does the heavy lifting for us by preventing IIS from terminating worker processes when there are any pending background work items. Consequently, HostingEnvironment.QueueBackgroundWorkItem is an ideal candidate for scheduling small background jobs in .NET 4.5.2.

For an overview of all the other new features that shipped with .NET 4.5.2, read the blog post from the .NET engineering team or the summary in the MSDN library: