ASP.NET MVC: O controlador é criado para cada solicitação?

112

Pergunta muito simples: os controladores no ASP.NET são criados para cada solicitação HTTP ou são criados na inicialização do aplicativo e reutilizados em todas as solicitações?

O controlador será criado apenas para uma solicitação HTTP específica?

Se minhas suposições anteriores estiverem corretas, posso confiar nisso? Quero criar um contexto de banco de dados (Entity Framework) que viverá apenas para uma solicitação. Se eu criá-lo como uma propriedade inicializada no construtor do controlador, será permitido que uma nova instância de contexto seja criada para cada solicitação?

Rasto
fonte
16
Coloque um ponto de interrupção em seu construtor e veja o que você pode descobrir ...
Greg B
10
@Greg B: ótima ideia, exceto que não vai me dizer se ele se comporta assim sempre - se as circunstâncias mudarem e algum controlador mudar seu comportamento, tenho um bug que pode ser realmente difícil de encontrar ...
Rasto
@drasto como você vai verificar se funciona assim sempre? Verifique todas as solicitações do seu aplicativo?
Greg B
4
@Todd Smith por favor algum link ou pelo menos o nome completo. As letras das árvores IoC são difíceis de pesquisar no Google. Obrigado.
Rasto
2
@drasto IoC = Inversão de controle en.wikipedia.org/wiki/Inversion_of_control
Bala R

Respostas:

103

Um controlador é criado para cada solicitação do ControllerFactory(que por padrão é o DefaultControllerFactory).

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

Observe que o Html.ActionHtml Helper criará outro controlador.

A versão resumida é que ControllerActivator.Createé chamado (para cada solicitação) para criar um Controlador (que inicia um novo Controlador através do DependencyResolver ou através do Ativador se nenhum Resolver tiver sido configurado):

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

A versão mais longa é esta (aqui está o código da fonte do 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));
    }
}

Este é o código de fábrica do controlador:

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

O que basicamente chama isso de:

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

Que chama este método no ControllerActivator(Este código tenta solicitar uma instância ao DependencyResolver ou apenas usa a classe Activator):

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

Isso pode envolver muita informação ... Mas eu queria mostrar que você realmente TEM um novo controlador para CADA solicitação.

Linkgoron
fonte
@Daniel @drasto aqui está a citação aspnet.codeplex.com/SourceControl/changeset/view/63930#266503
Bala R
32

Eu criei um construtor vazio para um controlador e coloquei um ponto de interrupção no construtor. Ele era atingido toda vez que havia um novo pedido. Acho que é criado para cada solicitação.

Bala R
fonte
3
1 Espero que você esteja certo, mas gostaria de algum conhecimento melhor aprovado do que apenas "em todos os casos em que tentei, funcionou". Se às vezes não funcionar assim por algum motivo, é um bug.
Rasto
6
@drasto: Não precisa se preocupar. O controlador é instanciado para cada solicitação. No entanto, parte da memória é reutilizada, mas você não deve se preocupar com o estado do controlador (se o seu tiver um). Ele será inicializado conforme o esperado. Mas pode haver uma situação em que mais de um controlador será instanciado. E é Html.RenderAction("action", "controller");
@RobertKoritnik & Bala R, tenho uma pergunta, por favor. O que acontece com os objetos criados como Aluno ou Lista <Aluno> depois que o método de ação os veiculou na visualização? Eles são eliminados? E o que acontece com esses objetos quando chega um novo pedido?
Mahdi Alkhatib
3

O controlador será criado quando qualquer ação em um controlador específico for executada.

Eu tenho um projeto onde todos os meus controladores herdam de um ApplicationControllere sempre que uma ação é executada, o ponto de interrupção é atingido dentro do ApplicationController- independentemente de seu controlador " atual ".

Eu inicializo meu agente (que funciona como meu contexto) sempre que meu controlador é criado assim:

    public IWidgetAgent widgetAgent { get; set; }

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

    }

Obviamente, isso não é o que você precisa - como você mencionou, só queria uma única instância cada vez que fosse chamada. Mas é um bom lugar para verificar o que está acontecendo a cada vez e garantir que outra instância do seu contexto não exista no momento.

Espero que isto ajude.

Rion Williams
fonte
2

Controladores são criados para cada solicitação. A mágica acontece no roteamento no gobal.aspx. Os caminhos de mapeamento direcionam MVC para qual controlador criar e ação no controlador para chamar e parâmetros para passar para eles.

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

Gelo preto
fonte
citação necessária - não é possível encontrar informações de apoio no documento vinculado. Obrigado
Rasto