"Cannot access a disposed object" on site restart

From time to time when recycling apppool/restarting site/rebuilding in VS we encounter the following exception in different flavors: “Cannot access a disposed object”.

How can we avoid this?

Example call stack:

IWebLog exception 'Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.'
Object name: 'AsyncDisposer'.: d669f1cf-d4ca-4bd4-9e2c-81133b7037ac System.ObjectDisposedException: Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'AsyncDisposer'.
   at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.AsyncDisposer.Dispose()
   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.<MoveNext>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<ExecuteSingletonAsyncQuery>d__21`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Litium.Web.Security.Identity.ApplicationUserStore.<>c__DisplayClass25_0.<<Fetch>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Litium.Application.Data.DbContextExtension.<ExecuteInDbContextAsync>d__4`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Litium.Web.Security.Identity.ApplicationUserStore.<Fetch>d__25.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Litium.Web.Security.Identity.ApplicationUserStore.<Get>d__24.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Litium.Web.Security.Cookies.ApplicationSecurityStampValidator.<>c__DisplayClass0_0.<<OnValidateIdentity>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Owin.Security.Cookies.CookieAuthenticationHandler.<AuthenticateCoreAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Owin.Security.Infrastructure.AuthenticationHandler.<BaseInitializeAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Owin.Security.Infrastructure.AuthenticationMiddleware`1.<Invoke>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Litium.Web.Security.Cookies.CookieReplicatorMiddleware.<Invoke>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNet.Identity.Owin.IdentityFactoryMiddleware`2.<Invoke>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Owin.Cors.CorsMiddleware.<Invoke>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Litium.Web.Runtime.GlobalExceptionMiddleware.<Invoke>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContextStage.<RunApp>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.<DoFinalWork>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar)
   at System.Web.HttpApplication.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Litium version: 6.2.2

This problem is often caused by the restart and is raised by the threads that is shutting down. If you look at the litiumstudio.log-file you have a column that is like [App:20] and the 20 (in my example) is the number of application domain recycling and you can compare that with the logs for the application starting and shutdown event to see if it is the old (application that is shutting down) or new (application that is starting) application that is causing it.

Seems to be the “old” application that is causing this in our case.

How can we solve this so that we can safely restart our live site without risking that the visitors are faced with an error page right after the restart?

To restart a live-site without any notice for the visitor you need multiple web-fronts that are load-balanced and during the restart you will move the current web-front out as an active server from the load-balancer…

Shouldn’t Litium be capable of shutting down gracefully without throwing errors instead of hiding the problem behind a load balancer and multiple web fronts?

Litium does not have an inbuilt function to stop inbound request to IIS and wait for the ongoing calls to complete before the application restart is initiated.

It’s possible to add an “block” function that will make the incoming request get an “Page is not reachable” message until the application have waited for all request (or a longer amount of time) to complete and can shutdown gracefully. But then we are not in the area “without risking that the visitors are faced with an error page” and to solve that you need to have multiple web-fronts.

So how is this supposed to work with deploying to a live site hosted on Litium drift, which doesn’t have any load balancing (on at least the small and medium server packages)?

We’ve noticed that Overlapped Recycle is activated on our app pool (which seems to be a default setting in IIS v.8 and up). Would it be safe to turn off Overlapped Recycle or could that cause bad side effects?

As long that you want the site restart not affecting the user (that was one requirement in the original question) it does not exists any way to do without to change hosting version to a version that includes multiple web fronts. If it is that you don’t want strange errors during deployment but a good message to the user that the site is under maintenance you can use the App_Offlinefeature in Web deploy for a message to the user. Taking Web Applications Offline with Web Deploy | Microsoft Docs

Yes, in most solution it can be good to turn off the Overlapped Recycle to avoid conflicts in example search index that the application that should shutdown can use on the same time that the application that is starting try to use. If you turn off the Overlapped Recycles IIS will ensure that the application that should shutdown have completed with that before the new application is created.

Thanks for the answer. Is there any sort of appShutdown event we can hook into to perform some cleanup prior to the website actually restarting?

https://docs.litium.com/documentation/architecture/application-lifecycle

That’s what I was looking for. Thanks.