Sesja null w konstruktorach kontrolera ASP.Net MVC

88

Dlaczego sesja ma wartość null w konstruktorach kontrolerów? Można uzyskać do niego dostęp z metod akcji. Przypuszczalnie, ponieważ struktura routingu MVC jest odpowiedzialna za utworzenie nowego kontrolera, po prostu nie utworzyła (ponownie) wystąpienia sesji w tym momencie.

Czy ktoś wie, czy jest to zgodne z projektem, a jeśli tak, to dlaczego?

[Udało mi się obejść problem, używając wzorca leniwego ładowania.]

Chris Arnold
źródło

Odpowiedzi:

79

Andrei ma rację - ma wartość null, ponieważ podczas uruchamiania w ramach ASP.NET MVC HttpContext (a tym samym HttpContext.Session) nie jest ustawiana, gdy klasa kontrolera jest konstruowana zgodnie z oczekiwaniami, ale jest ustawiana („wstrzykiwana”) później przez klasę ControllerBuilder. Jeśli chcesz lepiej zrozumieć cykl życia, możesz rozwinąć strukturę ASP.NET MVC (źródło jest dostępne) lub zapoznać się z: tą stroną

Jeśli potrzebujesz dostępu do sesji, jednym ze sposobów byłoby zastąpienie metody „OnActionExecuting” i dostęp do niej, ponieważ będzie ona dostępna do tego czasu.

Jednak, jak sugeruje Andrei, jeśli twój kod jest zależny od sesji, wtedy pisanie testów jednostkowych może być trudne, więc być może warto rozważyć umieszczenie sesji w klasie pomocniczej, którą można następnie zamienić na inną, wersja internetowa podczas uruchamiania w ramach testów jednostkowych, dlatego należy odłączyć kontroler od sieci.

Andrew W.
źródło
3
Nie jestem pewien, czy jest to właściwe stwierdzenie dotyczące HttpContext. Właściwie skonstruował się na początku całego przepływu. Możesz przeczytać trochę o szczegółowym przepływie tutaj beletsky.net/2011/06/inside-aspnet-mvc-route-to-mvchanlder.html lub możesz użyć reflektora i dowiedzieć się, kiedy utworzono wystąpienie httpContext - jest to około linii 1556 w httpruntime .cs.
Alexey Shcherbak
@AlexeyShcherbak Może być już skonstruowane - OP dotyczy tego, czy zostało ustawione we właściwości Session kontrolera MVC. tj. publiczna sesja HttpSessionStateBase {get; } on System.Web.Mvc.Controller To są różne rzeczy.
MemeDeveloper
61

Oprócz innych odpowiedzi tutaj, chociaż Controller.Sessionnie jest to wypełnione w konstruktorze, nadal możesz uzyskać dostęp do sesji poprzez:

System.Web.HttpContext.Current.Session

ze standardowym zastrzeżeniem, że potencjalnie zmniejsza to testowalność kontrolera.

Mike Chamberlain
źródło
3
Typ każdej z tych dwóch właściwości sesji jest inny, co może mieć znaczenie, jeśli zamierzasz zachować odniesienie do samego stanu sesji.
BrianCooksey
@BrianCooksey what's different?
MichaelMao
1
Controller.Session jest typu System.Web.HttpSessionStateBase (patrz msdn.microsoft.com/en-us/library/… ), ale System.Web.HttpContext.Current.Session jest typu System.Web.SessionState.HttpSessionState (patrz msdn .microsoft.com / en-us / library /… )
BrianCooksey
Stara odpowiedź, ale chciałem powiedzieć, że System.Web.HttpContext.Current.Sessionjest również nullw instancjatorze VS2019 MVC.
jp2code
11

Sesja jest wstrzykiwana później w cyklu życia. Dlaczego i tak potrzebujesz sesji w konstruktorze? Jeśli potrzebujesz go do TDD, powinieneś zawinąć sesję w obiekt, który można wyrejestrować.

Andrei Rînea
źródło
1
Aby dodać Andreiowi Rinei, oto konkretny przykład wspomnianej przez niego techniki: iridescence.no/post/…
murki
4
Chcę uzyskać dostęp do sesji podczas moich konstruktorów, aby mieć dostęp do wcześniej zapisanych informacji o sesji. Tak, mógłbym zastąpić metodę OnActionExecuting, ale z pewnością nie jest to eleganckie rozwiązanie.
Chris Arnold
8

Możesz zastąpić metodę Initialize, aby ustawić sesję.

protected override void Initialize(RequestContext requestContext)
Funlover
źródło
2

Jeśli używasz kontenera IoC, spróbuj wstrzyknąć i użyć HttpSessionStateBasezamiast Sessionobiektu:

private static Container defaultContainer()
{
    return new Container(ioc =>
    {
        // session manager setup
        ioc.For<HttpSessionStateBase>()
           .Use(ctx => new HttpSessionStateWrapper(HttpContext.Current.Session)); 
    });
}
VahidN
źródło
2

Ta odpowiedź może być przydatna dla niektórych osób

Jeśli nadpisujemy metodę Initialize, musimy zainicjować klasę bazową z kontekstem żądania: base.Initialize (requestContext);

protected override void Initialize(RequestContext requestContext)
        {
            base.Initialize(requestContext);
           

        }
Prashanth vunnam gcs
źródło
Przydatny. Zwróć uwagę, że podpis metody protected override void Initialize(System.Web.Routing.RequestContext requestContext).
Martin_W