ASP MVC: ¿Cuándo se llama a IController Dispose ()?

83

Estoy pasando por una gran refactorización / ajuste de velocidad de una de mis aplicaciones MVC más grandes. Se ha implementado en producción desde hace unos meses y estaba empezando a tener tiempos de espera esperando conexiones en el grupo de conexiones. He rastreado el problema hasta que las conexiones no se eliminan correctamente.

A la luz de eso, desde entonces hice este cambio en mi controlador base:

public class MyBaseController : Controller
{
    private ConfigurationManager configManager;  // Manages the data context.

    public MyBaseController()
    {
         configManager = new ConfigurationManager();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (this.configManager != null)
            {
                this.configManager.Dispose();
                this.configManager = null;
            }
        }

        base.Dispose(disposing);
    }
}

Ahora tengo dos preguntas:

  1. ¿Estoy introduciendo una condición de carrera? Dado que configManageradministra el DataContextque expone los IQueryable<>parámetros a las vistas, necesito asegurarme de que Dispose()no se llamará en el controlador antes de que la vista termine de renderizarse.
  2. ¿El marco MVC llama Dispose()al controlador antes o después de que se renderice la vista? ¿O el marco MVC deja eso en manos de GarbageCollector?
John Gietzen
fuente
2
¡Estoy ansioso por la respuesta a esta! ¡GRAN pregunta!
Daniel Elliott
Sin mirar otro código (el suyo o de ASP.NET MVC ...) ¿por qué exactamente necesita anular el configManager? ¿Eso ayuda en algo? Piense detenidamente antes de que cualquiera de ustedes me "DUH" ..
Andrei Rînea
Me refiero a que en un caso general como ese, una condición de carrera puede eliminarse fácilmente así. En este caso particular, dudo que una instancia de controlador sea utilizada por más de un subproceso y, por lo tanto, no existe ningún riesgo de condición de carrera.
Andrei Rînea
1
@Andrei: Es solo un poco de codificación defensiva. Me impide deshacerme de la conexión de la base de datos dos veces, si se llama dos veces a mi método de eliminación.
John Gietzen
1
@Andrei: Bueno, en mi opinión "Ignorar" y "Llamar a Dispose en objetos secundarios de todos modos" son completamente diferentes. De ahí el cheque.
John Gietzen

Respuestas:

70

Se llama a Dispose después de renderizar la vista, siempre .

La vista se representa en la llamada a ActionResult.ExecuteResult. Eso es llamado (indirectamente) por ControllerActionInvoker.InvokeAction, que a su vez es llamado por ControllerBase.ExecuteCore.

Dado que el controlador está en la pila de llamadas cuando se representa la vista, no se puede eliminar en ese momento.

Craig Stuntz
fuente
Genial, ¿tienes documentación? Solo quiero estar seguro.
John Gietzen
¡Excelente! Sería genial encontrar un médico que lo explique. Pero la respuesta ampliada fue realmente reconfortante. El código es el mejor documento en absoluto. : D
CSA
37

Solo para ampliar la respuesta de Craig Stuntz :

ControllerFactory maneja cuando se elimina un controlador. Al implementar la interfaz IControllerFactory, uno de los métodos que debe implementarse es ReleaseController.

No estoy seguro de qué ControllerFactory está usando, ya sea que haya rodado el suyo, pero en Reflector mirando DefaultControllerFactory, el método ReleaseController se implementa así:

public virtual void ReleaseController(IController controller)
{
    IDisposable disposable = controller as IDisposable;
    if (disposable != null)
    {
        disposable.Dispose();
    }
}

Se pasa una referencia de IController, si ese controlador implementa IDisposable, entonces se llama al método Dispose de los controladores. Por lo tanto, si tiene algo que necesite eliminar después de que finalice la solicitud, que es después de que se procese la vista. Herede IDisposable y ponga su lógica en el método Dispose para liberar cualquier recurso.

El método ReleaseController es llamado por System.Web.Mvc.MvcHandler que maneja la solicitud e implementa IHttpHandler. ProcessRequest toma el HttpContext que se le ha dado e inicia el proceso de encontrar el controlador para manejar la solicitud, llamando al ControllerFactory implementado. Si observa el método ProcessRequest, verá el bloque finalmente que llama al ReleaseController de ControllerFactory. Esto solo se llama cuando el controlador ha devuelto un ViewResult.

Dale Ragan
fuente
Respuesta impresionante. No pude entender por qué una instancia directa de un objeto Controller no me permitiría llamar a Dispose () en él, pero parece que necesito crear una nueva instancia usando la interfaz IDisposable. ¡Esto funcionó para mí!
MegaMatt
Entonces ... ¿ HttpContextes un hombre? Ahora estoy realmente confundido.
Chef_Code