C# / .NETDevOpsMisc
C# / .NET
Background tasks in MVC and IIS
Alexandru Puiu
Alexandru Puiu
October 08, 2013
1 min

As you might’ve noticed, keeping threads running after a request returns, for processing post operational tasks (such as performing analytics on a file that was uploaded, etc) don’t always complete in a web project. There are several issues with spawning threads in the context of an ASP.NET project. Phil Haack’s post explains the issues in more detail. The following classes solve the problem of IIS killing threads before they complete. First part is the IISTaskManager:

using NLog;
using System;
using System.Threading.Tasks;
using System.Web.Hosting;
namespace Web.Models
{
///
/// Static class for running background tasks in IIS.
/// Any spawned threads are registered properly with IIS
/// to ensure they finish execution.
/// 
public static class IISTaskManager
{
///
/// Runs a background task that is registered with the hosting environment
/// so it is guaranteed to finish executing.
/// 
/// The lambda expression to invoke.
public static void Run(Action action)
{
new IISBackgroundTask().DoWork(action);
}
///
/// Generic object for completing tasks in a background thread
/// when the request doesn't need to wait for the results
/// in the response.
/// 
class IISBackgroundTask : IRegisteredObject
{
///
/// Constructs the object and registers itself with the hosting environment.
/// 
public IISBackgroundTask()
{
HostingEnvironment.RegisterObject(this);
}
///
/// Called by IIS, once with  set to false
/// and then again with  set to true.
/// 
void IRegisteredObject.Stop(bool immediate)
{
if (_task.IsCompleted || _task.IsCanceled || _task.IsFaulted || immediate)
{
// Task has completed or was asked to stop immediately,
// so tell the hosting environment that all work is done.
HostingEnvironment.UnregisterObject(this);
}
}
///
/// Invokes the  as a Task.
/// Any exceptions are logged
/// 
/// The lambda expression to invoke.
public void DoWork(Action action)
{
try
{
_task = Task.Run(action);
}
catch (AggregateException ex)
{
// Log exceptions
foreach (var innerEx in ex.InnerExceptions)
{
_logger.ErrorException(innerEx.ToString(), innerEx);
}
}
catch (Exception ex)
{
_logger.ErrorException(ex.ToString(), ex);
}
}
private Task _task;
private static Logger _logger = LogManager.GetCurrentClassLogger();
}
}
}

Usage:

IISTaskManager.Run(() =>
  {
    //Run any piece of code which needs to run in a new thread
  }
);

Tags

utils
Alexandru Puiu

Alexandru Puiu

Engineer / Security Architect

Systems Engineering advocate, Software Engineer, Security Architect / Researcher, SQL/NoSQL DBA, and Certified Scrum Master with a passion for Distributed Systems, AI and IoT..

Expertise

.NET
RavenDB
Kubernetes

Social Media

githubtwitterwebsite

Related Posts

RavenDB Integration Testing
Using RavenDB in Integration Testing
December 24, 2022
2 min

Subscribe To My Newsletter

I'll only send worthwhile content I think you'll want, less than once a month, and promise to never spam or sell your information!
© 2023, All Rights Reserved.

Quick Links

Get In TouchAbout Me

Social Media