ASP.NET MVC: czy kontroler jest tworzony dla każdego żądania?

112

Bardzo proste pytanie: czy kontrolery w ASP.NET są tworzone dla każdego żądania HTTP, czy też są tworzone podczas uruchamiania aplikacji i ponownie wykorzystywane we wszystkich żądaniach?

Czy kontroler zostanie utworzony tylko dla konkretnego żądania HTTP?

Jeśli moje wcześniejsze założenia są prawidłowe, czy mogę na tym polegać? Chcę utworzyć kontekst bazy danych (Entity Framework), który będzie działać tylko dla jednego żądania. Jeśli utworzę ją jako właściwość zainicjowaną w konstruktorze kontrolera, czy zostanie przyznane, że nowe wystąpienie kontekstu zostanie utworzone dla każdego żądania?

Rasto
źródło
16
Umieść punkt przerwania w swoim konstruktorze i zobacz, czego możesz się dowiedzieć ...
Greg B
10
@Greg B: świetny pomysł, ale nie powie mi, czy zachowuje się tak zawsze - jeśli zmienią się okoliczności i jakiś kontroler zmieni swoje zachowanie, mam błąd, który może być naprawdę trudny do znalezienia ...
Rasto
@drasto jak sprawdzisz, czy zawsze tak działa? Sprawdzasz każde zgłoszenie do swojej aplikacji?
Greg B
4
@Todd Smith, proszę o jakiś link lub przynajmniej imię i nazwisko. Litery drzewa IoC są trudne do wygooglowania. Dziękuję Ci.
Rasto,
2
@drasto IoC = Inversion of control en.wikipedia.org/wiki/Inversion_of_control
Bala R

Odpowiedzi:

103

Kontroler jest tworzony dla każdego żądania przez ControllerFactory(domyślnie DefaultControllerFactory).

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

Zwróć uwagę, że Html.Actionpomocnik Html utworzy inny kontroler.

Krótka wersja jest ControllerActivator.Createwywoływana (dla każdego żądania) w celu utworzenia kontrolera (który inicjuje nowy kontroler za pośrednictwem DependencyResolver lub aktywatora, jeśli nie skonfigurowano Resolvera):

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

Dłuższa wersja jest następująca (oto kod ze źródła z 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));
    }
}

Oto kod fabryczny kontrolera:

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

Co w zasadzie nazywa to:

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

Który wywołuje tę metodę w ControllerActivator(ten kod próbuje zapytać DependencyResolver o instancję lub po prostu używa klasy Activator):

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

To może zawierać zbyt wiele informacji ... Ale chciałem pokazać, że naprawdę otrzymujesz nowy kontroler do KAŻDEJ prośby.

Linkgoron
źródło
32

Utworzyłem pusty konstruktor dla kontrolera i umieściłem punkt przerwania w konstruktorze. Został trafiony za każdym razem, gdy było nowe żądanie. Więc myślę, że jest tworzony na każde żądanie.

Bala R
źródło
3
+1 Mam nadzieję, że masz rację, ale chciałbym mieć bardziej zatwierdzoną wiedzę niż tylko „we wszystkich przypadkach, w których próbowałem działać”. Jeśli czasami z jakiegoś powodu tak nie działa, oznacza to błąd.
Rasto,
6
@drasto: Nie musisz się martwić. Kontroler jest tworzony dla każdego żądania. Część pamięci zostaje jednak ponownie wykorzystana, ale nie powinieneś martwić się o stan kontrolera (jeśli twój ma taki). Zostanie zainicjowany zgodnie z oczekiwaniami. Może jednak zaistnieć sytuacja, w której zostanie utworzony wystąpienie więcej niż jednego kontrolera. I wtedy widoki wywołują akcje kontrolera (tj. Html.RenderAction("action", "controller");)
Robert Koritnik
@RobertKoritnik & Bala R, mam pytanie. Co dzieje się z obiektami utworzonymi, takimi jak Student lub List <Student>, po tym, jak metoda akcji podała je lub je do widoku? Czy zostaną usunięte? A co się dzieje z tymi obiektami, gdy przychodzi nowe żądanie?
Mahdi Alkhatib
3

Kontroler zostanie utworzony, gdy zostanie wykonana dowolna akcja w określonym kontrolerze.

Mam projekt, w którym wszystkie moje kontrolery dziedziczą po ApplicationControlleri za każdym razem, gdy wykonywana jest akcja, punkt przerwania jest trafiany wewnątrz ApplicationController- niezależnie od jego „ bieżącego ” kontrolera.

Inicjalizuję mojego agenta (który działa jako mój kontekst) za każdym razem, gdy mój kontroler jest tworzony w następujący sposób:

    public IWidgetAgent widgetAgent { get; set; }

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

    }

To oczywiście nie jest to, czego potrzebujesz - jak wspomniałeś, że chciałeś mieć tylko jedną instancję za każdym razem, gdy jest wywoływana. Ale jest to dobre miejsce, aby za każdym razem sprawdzić, co się dzieje i upewnić się, że inna instancja kontekstu obecnie nie istnieje.

Mam nadzieję że to pomoże.

Rion Williams
źródło
2

Kontrolery są tworzone na każde żądanie. Magia dzieje się w routingu w gobal.aspx. Ścieżki mapowania kierują MVC do kontrolera, który ma zostać utworzony i akcji na kontrolerze do wywołania, oraz parametrów do przekazania do nich.

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

Czarny lód
źródło
potrzebne źródło - nie można znaleźć dodatkowych informacji w połączonym dokumencie. Dziękuję
Rasto