As already stated, it is highly not recommended to have application persist large data during Application_End
, as this might be triggered when application pool is shut down, not at the beginning of its shutting down. This is explained in more detailed (but still short) here.
If one still wants to catch the shutting down of an application pool for all cases that do not instantly kill it, can do the following:
Dirty way
1) Get shutdown time limit - IIS manager -> Application Pools -> -> Advanced settings ... -> Process model group -> Shutdown Time Limit (second)
2) Create an thread/task in the application to run twice as often as shutdown time limit to ensure that "shutting down" state can be caught. This thread should
i) check if application pool is shutting down
public bool IsShuttingDown()
{
return System.Web.Hosting.HostingEnvironment.ShutdownReason != ApplicationShutdownReason.None;
}
ii) if shutting down is on, do your stuff. Ensure that stuff is executed only once
More correct way (as indicated here)
1) Create a class which implements IRegisteredObject
public class HostingEnvironmentRegisteredObject : IRegisteredObject
{
// this is called both when shutting down starts and when it ends
public void Stop(bool immediate)
{
if (immediate)
return;
// shutting down code here
// there will about Shutting down time limit seconds to do the work
}
}
2) Register your object (Global.asax.cs
)
protected void Application_Start()
{
// other initialization code here
HostingEnvironment.RegisterObject(new HostingEnvironmentRegisteredObject());
}
3) Unregistering (source)
The Stop method is first called with the immediate parameter set to
false. The object can either complete processing, call the
UnregisterObject method, and then return or it can return immediately
and complete processing asynchronously before calling the
UnregisterObject method.
If the registered object does not complete processing before the
application manager's time-out period expires, the Stop method is
called again with the immediate parameter set to true. When the
immediate parameter is true, the registered object must call the
UnregisterObject method before returning; otherwise, its
registration will be removed by the application manager.
As a side note, I will also include some details about implementing the same think within ASP.NET Core 2.x. This can be performed using IApplicationLifetime interface. Example (Startup.cs):
public void Configure(IApplicationLifetime lifetime)
{
var quartz = new JobScheduler(Kernel);
lifetime.ApplicationStarted.Register(quartz.Start);
lifetime.ApplicationStopping.Register(quartz.Stop);
}
Note: this will work only when hosted within IIS (possible other Web servers), but not within IISExpress which will not trigger IApplicationLifetime
functionality.