ASP.NET MVC: le contrôleur est-il créé pour chaque demande?

112

Question très simple: les contrôleurs dans ASP.NET sont-ils créés pour chaque requête HTTP ou sont-ils créés au démarrage de l'application et réutilisés tout au long des requêtes?

Le contrôleur sera-t-il créé uniquement pour une requête HTTP particulière?

Si mes hypothèses précédentes sont correctes, puis-je m'en remettre? Je souhaite créer un contexte de base de données (Entity Framework) qui ne vivra que pour une seule demande. Si je le crée en tant que propriété initialisée dans le constructeur du contrôleur, est-il autorisé qu'une nouvelle instance de contexte soit créée pour chaque demande?

Rasto
la source
16
Mettez un point d'arrêt dans votre constructeur et voyez ce que vous pouvez trouver ...
Greg B
10
@Greg B: super idée sauf qu'il ne me dira pas s'il se comporte toujours comme ça - si les circonstances changent et qu'un contrôleur changera de comportement j'ai un bug qui pourrait être vraiment difficile à trouver ...
Rasto
@drasto comment allez-vous vérifier si cela fonctionne toujours comme ça? Vérifiez chaque demande de votre application?
Greg B
4
@Todd Smith s'il vous plaît un lien ou au moins le nom complet. Les lettres d'arbre IoC sont difficiles à google. Je vous remercie.
Rasto
2
@drasto IoC = Inversion de contrôle en.wikipedia.org/wiki/Inversion_of_control
Bala R

Réponses:

103

Un contrôleur est créé pour chaque requête par le ControllerFactory(qui par défaut est le DefaultControllerFactory).

http://msdn.microsoft.com/en-us/library/system.web.mvc.defaultcontrollerfactory.aspx

Notez que Html.ActionHtml Helper créera un autre contrôleur.

La version courte ControllerActivator.Createest appelée (pour chaque requête) pour créer un contrôleur (qui entre dans un nouveau contrôleur soit via le DependencyResolver, soit via l'activateur si aucun résolveur n'a été configuré):

public IController Create(RequestContext requestContext, Type controllerType) 
{
    try 
    {
        return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
    }

La version plus longue est la suivante (voici le code de la source du MvcHandler):

protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
    SecurityUtil.ProcessInApplicationTrust(() =>
    {
        IController controller;
        IControllerFactory factory;
        ProcessRequestInit(httpContext, out controller, out factory);

        try
        {
            controller.Execute(RequestContext);
        }
        finally
        {
            factory.ReleaseController(controller);
        }
    });
}

private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
    // non-relevant code
    // Instantiate the controller and call Execute
    factory = ControllerBuilder.GetControllerFactory();
    controller = factory.CreateController(RequestContext, controllerName);
    if (controller == null)
    {
        throw new InvalidOperationException(
            String.Format(
                CultureInfo.CurrentCulture,
                MvcResources.ControllerBuilder_FactoryReturnedNull,
                factory.GetType(),
                controllerName));
    }
}

Voici le code d'usine du contrôleur:

public virtual IController CreateController(RequestContext requestContext, string controllerName) 
{
    Type controllerType = GetControllerType(requestContext, controllerName);
    IController controller = GetControllerInstance(requestContext, controllerType);
    return controller;
}

Ce qui appelle essentiellement ceci:

protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType) 
{
    return ControllerActivator.Create(requestContext, controllerType);
}

Qui appelle cette méthode dans le ControllerActivator(Ce code essaie de demander au DependencyResolver une instance, ou utilise simplement la classe Activator):

public IController Create(RequestContext requestContext, Type controllerType) 
{
    try 
    {
        return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
    }

Cela pourrait tomber sous trop d'informations ... Mais je voulais montrer que vous obtenez vraiment un nouveau contrôleur pour CHAQUE demande.

Linkgoron
la source
@Daniel @drasto voici la citation aspnet.codeplex.com/SourceControl/changeset/view/63930#266503
Bala R
32

J'ai créé un constructeur vide pour un contrôleur et mis un point d'arrêt dans le constructeur. Il était touché à chaque fois qu'il y avait une nouvelle demande. Je pense donc qu'il est créé pour chaque demande.

Bala R
la source
3
+1 J'espère que vous avez raison, mais j'aimerais avoir des connaissances mieux approuvées que simplement "dans tous les cas où j'ai essayé, cela a fonctionné". Si parfois cela ne fonctionne pas comme ça pour une raison quelconque, cela signifie un bug.
Rasto
6
@drasto: Pas besoin de s'inquiéter. Le contrôleur est instancié pour chaque requête. Cependant, une partie de la mémoire est réutilisée, mais vous ne devriez pas vous soucier de l'état du contrôleur (si le vôtre en a un). Il sera initialisé comme prévu. Mais il peut y avoir une situation où plus d'un contrôleur sera instancié. Et c'est à ce moment que visualise les actions du contrôleur d'appel (c'est-à-dire Html.RenderAction("action", "controller");)
Robert Koritnik
@RobertKoritnik & Bala R, j'ai une question s'il vous plaît. Que se passe-t-il pour les objets créés comme Student ou List <Student> après que la méthode d'action les a servis à la vue? Sont-ils disposés? Et que se passe-t-il pour ces objets lorsqu'une nouvelle demande survient?
Mahdi Alkhatib
3

Le contrôleur sera créé lorsqu'une action dans un contrôleur spécifique est effectuée.

J'ai un projet où tous mes contrôleurs héritent d'un ApplicationControlleret chaque fois qu'une action est effectuée, le point d'arrêt est atteint à l'intérieur du ApplicationController- quel que soit son contrôleur " actuel ".

J'initialise mon agent (qui fonctionne comme mon contexte) chaque fois que mon contrôleur est créé comme tel:

    public IWidgetAgent widgetAgent { get; set; }

    public WidgetController()
    {
        if (widgetAgent == null)
        {
            widgetAgent = new WidgetAgent();
        }

    }

Ce n'est évidemment pas ce dont vous avez besoin - comme vous l'avez mentionné, vous ne vouliez qu'une seule instance à chaque fois qu'elle était appelée. Mais c'est un bon endroit pour vérifier ce qui se passe à chaque fois et pour s'assurer qu'une autre instance de votre contexte n'existe pas actuellement.

J'espère que cela t'aides.

Rion Williams
la source
2

Des contrôleurs sont créés pour chaque demande. La magie se produit dans le routage dans le gobal.aspx. Les chemins de mappage dirigent MVC vers le contrôleur à créer et l'action sur le contrôleur à appeler, ainsi que les paramètres à leur transmettre.

http://www.asp.net/mvc/tutorials/asp-net-mvc-routing-overview-vb

Glace noir
la source
citation nécessaire - impossible de trouver des informations à l'appui dans le document lié. Merci
Rasto