4
votes

Why is my controller being disposed after the await statement?

    public async Task<ViewResult> MyAction()
    {
            await Task.Yield();

            // controller disposed at this point... why?

            return this.View();
    }

My controller uses resources that it disposes by overriding the Dispose method... but using async/await seems to break this, because it disposes the controller as soon as the await statement is executed... how can I dispose resources in a way that async/await is supported?

EDIT: Last methods in callstack of the dispose method:

MyWebRole.dll!MyWebRole.Code.MyController.Dispose(bool disposing = true) Line 102   C#
System.Web.Mvc.dll!System.Web.Mvc.Controller.Dispose() + 0x25 bytes   
System.Web.Mvc.dll!System.Web.Mvc.DefaultControllerFactory.ReleaseController(System.Web.Mvc.IController controller = {MyWebRole.Areas.App.Controllers.LongPollingController}) + 0x3e bytes   
System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.BeginProcessRequest.AnonymousMethod__5() + 0x70 bytes   
System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncResultWrapper.MakeVoidDelegate.AnonymousMethod__0() + 0x2d bytes   
System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncResultWrapper.BeginSynchronous<System.Web.Mvc.Async.AsyncVoid>.AnonymousMethod__7(System.IAsyncResult _ = {System.Web.Mvc.Async.SimpleAsyncResult}) + 0x2b bytes   
System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult<System.Web.Mvc.Async.AsyncVoid>.End() + 0x99 bytes   
System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncResultWrapper.End<System.Web.Mvc.Async.AsyncVoid>(System.IAsyncResult asyncResult = {System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult<System.Web.Mvc.Async.AsyncVoid>}, object tag = {object}) + 0x3c bytes   
System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncResultWrapper.End(System.IAsyncResult asyncResult = {System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult<System.Web.Mvc.Async.AsyncVoid>}, object tag = {object}) + 0x29 bytes   
System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.EndProcessRequest.AnonymousMethod__d() + 0x27 bytes   
System.Web.Mvc.dll!System.Web.Mvc.SecurityUtil.GetCallInAppTrustThunk.AnonymousMethod__0(System.Action f = {Method = {System.Reflection.RuntimeMethodInfo}}) + 0x20 bytes   
System.Web.Mvc.dll!System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(System.Action action = {Method = {System.Reflection.RuntimeMethodInfo}}) + 0x3e bytes   
System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.EndProcessRequest(System.IAsyncResult asyncResult = {System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult<System.Web.Mvc.Async.AsyncVoid>}) + 0x77 bytes   
System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(System.IAsyncResult result = {System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult<System.Web.Mvc.Async.AsyncVoid>}) + 0x27 bytes   

The Dispose method is being called with disposing = true... thats bad. Maybe a bug?

EDIT 2: I have tryed subclassing AsyncController but the same happens.

Any work around?

3
Is a reference retained to the controller after the await statement? - Zipper
Only the this itself... it is the same object before and after the await. - Miguel Angelo
It is the same... but disposed. - Miguel Angelo
Is another thread calling dispose on the object? Have you tried setting a break point on the dispose function and see who's calling it? - Zipper
Edited question to include callstack of dispose method... it is MVC that calls it. - Miguel Angelo

3 Answers

6
votes

The call stack shown in the question is from MVC3 and not MVC4. Unfortunately MVC3 does not support Task-based async calls. When you call await the task may very well be started but MVC3 doesn't know anything about that, so it just calls ToString() on it and returns it as a ContentResult.

The Task-based async support was added in MVC4, and in fact doesn't even require deriving from AsyncController. All Controllers in MVC4 support sync, and Task-based async. You only need AsyncController if you use the Async/Completed pattern.

0
votes

Based on the stack trace it appears that whatever started the process request decided it to end it, thus you lose the reference to the controller and it's eligible for garbage collection/disposing. I would look in to what started the process request and why it would be ending it.

0
votes

I had this issue and it troubled me for a full day. I finally realised the ActionInvoker I was hooking up in my ControllerFactory was inheriting from ControllerActionInvoker. When I changed that to AsyncControllerActionInvoker the problem went away.