2009-09-04 13 views
71

Je suis en train de refaire un gros refactoring/speed de l'une de mes plus grosses applications MVC. Il a été déployé en production depuis quelques mois maintenant, et je commençais à recevoir des délais d'attente pour les connexions dans le pool de connexions. J'ai suivi le problème jusqu'à ce que les connexions ne soient pas éliminées correctement.ASP MVC: Quand IController Dispose() est appelé?

À la lumière de cela, j'ai depuis fait ce changement à mon contrôleur de 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); 
    } 
} 

Maintenant, j'ai deux questions:

  1. Suis-je l'introduction d'une condition de course? Depuis le configManager gère le DataContext qui expose IQueryable<> paramètres à les vues, je dois vous assurer que Dispose() ne sera pas appelé sur le contrôleur avant que la vue termine le rendu.
  2. Le cadre MVC appelle-t-il Dispose() sur le contrôleur avant ou après le rendu de la vue? Ou, le framework MVC laisse-t-il le au GarbageCollector?
+1

Je suis tellement impatient de répondre à celui-ci! GRANDE question! –

+0

Sans regarder autre code (le vôtre ou ASP.NET MVC ..) pourquoi exactement devez-vous annuler le configManager? Cela aide-t-il quelque chose? Réfléchissez bien avant qu'aucun d'entre vous "DUH" moi .. –

+0

Je veux dire dans un cas général comme celui d'une condition de course peut facilement être enlevé comme ça. Dans ce cas particulier, je doute qu'une instance de contrôleur soit utilisée par plus d'un thread et qu'il n'y ait donc aucun risque de condition de concurrence. –

Répondre

60

Dispose est appelé après la vue est rendue, toujours.

La vue est rendue dans l'appel à ActionResult.ExecuteResult. Cela s'appelle (indirectement) par ControllerActionInvoker.InvokeAction, qui est à son tour appelé par ControllerBase.ExecuteCore. Étant donné que le contrôleur est dans la pile d'appels lorsque la vue est rendue, il ne peut pas être éliminé à ce moment-là.

+0

Super, avez-vous de la documentation? Je veux juste être sûr. –

+0

La documentation est le code source MVC. Voir la réponse étendue. –

+0

Parfait. Je vous remercie! –

32

Juste développiez Craig Stuntz's Answer:

Les poignées ControllerFactory lorsqu'un contrôleur est disposé. Lors de l'implémentation de l'interface IControllerFactory, l'une des méthodes à implémenter est ReleaseController.

Je ne sais pas ce que ControllerFactory vous utilisez, que vous rouliez votre propre, mais réflecteur regardant la DefaultControllerFactory, la méthode ReleaseController est mis en œuvre comme ceci:

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

Une référence iController est transmis, Si ce contrôleur implémente IDisposable, la méthode Dispose des contrôleurs est appelée. Donc, si vous avez quelque chose dont vous avez besoin après la fin de la requête, c'est après que la vue est rendue. Héritez de IDisposable et placez votre logique dans la méthode Dispose pour libérer toutes les ressources.

La méthode ReleaseController est appelée par System.Web.Mvc.MvcHandler qui gère la requête et implémente IHttpHandler. ProcessRequest prend le HttpContext qui lui est donné et lance le processus de recherche du contrôleur pour gérer la requête, en appelant dans le ControllerFactory implémenté. Si vous regardez dans la méthode ProcessRequest, vous verrez le bloc finally qui appelle le ReleaseController du ControllerFactory. Ceci n'est appelé que lorsque le contrôleur a renvoyé un ViewResult.

+0

Super réponse. Je ne pouvais pas comprendre pourquoi une instance directe d'un objet Controller ne me laisserait pas appeler Dispose() dessus, mais il semblerait que je doive créer une nouvelle instance en utilisant l'interface IDisposable. Cela a fonctionné pour moi! –

+0

Alors ... 'HttpContext' est un mâle? Maintenant, je suis vraiment confus. –