Session null dans les constructeurs de contrôleurs ASP.Net MVC

88

Pourquoi la session est-elle nulle dans les constructeurs des contrôleurs? Il est accessible à partir des méthodes Action. Vraisemblablement, parce que le framework MVC Routing est responsable de la création d'un contrôleur, il n'a tout simplement pas (ré) instancié la session à ce stade.

Est-ce que quelqu'un sait si c'est intentionnel et, si oui, pourquoi?

[J'ai réussi à contourner le problème en utilisant un modèle de chargement différé.]

Chris Arnold
la source

Réponses:

78

Andrei a raison - il est nul car lors de l'exécution sous le framework ASP.NET MVC, HttpContext (et donc HttpContext.Session) n'est pas défini lorsque la classe de contrôleur est construite comme vous pouvez vous y attendre, mais il est défini ("injecté") plus tard par la classe ControllerBuilder. Si vous voulez une meilleure compréhension du cycle de vie, vous pouvez soit dérouler le framework ASP.NET MVC (la source est disponible), soit vous référer à: cette page

Si vous avez besoin d'accéder à la session, une façon serait de remplacer la méthode "OnActionExecuting" et d'y accéder, car elle sera disponible à ce moment-là.

Cependant, comme le suggère Andrei, si votre code dépend de la session, il pourrait être difficile d'écrire des tests unitaires, vous pourriez peut-être envisager d'encapsuler la session dans une classe d'assistance qui peut ensuite être échangée contre une autre, non- version web lors de l'exécution sous tests unitaires, donc découpler votre contrôleur du web.

Andrew W
la source
2
Je ne suis pas sûr que ce soit une déclaration correcte sur HttpContext. Il s'est en fait construit au début du flux entier. Vous pouvez lire un peu plus sur le flux détaillé ici beletsky.net/2011/06/inside-aspnet-mvc-route-to-mvchanlder.html ou vous pouvez utiliser le réflecteur et vous retrouver lorsque httpContext a été instancié - c'est autour de la ligne 1556 dans httpruntime .cs.
Alexey Shcherbak
@AlexeyShcherbak Il peut être déjà construit - OP consiste à savoir s'il a été défini sur la propriété Session du contrôleur MVC. ie Session publique HttpSessionStateBase {get; } sur System.Web.Mvc.Controller Ce sont des choses différentes.
MemeDeveloper
61

En plus des autres réponses ici, bien qu'il Controller.Sessionne soit pas renseigné dans le constructeur, vous pouvez toujours accéder à la session via:

System.Web.HttpContext.Current.Session

avec l'avertissement standard que cela réduit potentiellement la testabilité de votre contrôleur.

Mike Chamberlain
la source
3
Le type de chacune de ces deux propriétés de session est différent, ce qui peut être important si vous avez l'intention de conserver une référence à l'état de session lui-même.
BrianCooksey
@BrianCooksey qu'est-ce qui est différent?
MichaelMao
1
Controller.Session est de type System.Web.HttpSessionStateBase (voir msdn.microsoft.com/en-us/library/… ) mais System.Web.HttpContext.Current.Session est de type System.Web.SessionState.HttpSessionState (voir msdn .microsoft.com / en-us / library /… )
BrianCooksey
10

La session est injectée plus tard dans le cycle de vie. Pourquoi avez-vous besoin de la session dans le constructeur de toute façon? Si vous en avez besoin pour TDD, vous devez envelopper la session dans un objet moquable.

Andrei Rînea
la source
1
Pour ajouter à Andrei Rinea, voici un exemple spécifique de la technique mentionnée par lui: iridescence.no/post/…
murki
4
Je souhaite accéder à la session pendant mes constructeurs afin de pouvoir accéder aux informations de session précédemment stockées. Oui, je pourrais remplacer la méthode OnActionExecuting, mais ce n'est certainement pas une solution élégante.
Chris Arnold
8

Vous pouvez remplacer la méthode Initialize pour définir votre session.

protected override void Initialize(RequestContext requestContext)
Amoureux
la source
2

Si vous utilisez un conteneur IoC, essayez d'injecter et d'utiliser le HttpSessionStateBaseau lieu de l' Sessionobjet:

private static Container defaultContainer()
{
    return new Container(ioc =>
    {
        // session manager setup
        ioc.For<HttpSessionStateBase>()
           .Use(ctx => new HttpSessionStateWrapper(HttpContext.Current.Session)); 
    });
}
VahidN
la source
2

Cette réponse peut être utile pour certaines personnes

Si nous surchargons la méthode Initialize, nous devons initialiser la classe de base avec le contexte de requête: base.Initialize (requestContext);

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

        }
Prashanth Vunnam GC
la source
Utile. Notez que la signature de la méthode protected override void Initialize(System.Web.Routing.RequestContext requestContext).
Martin_W