Sesión nula en ASP.Net MVC Controller Constructors

88

¿Por qué Session es nula en los constructores de Controllers? Se puede acceder desde los métodos de acción. Presumiblemente, debido a que el marco de enrutamiento MVC es responsable de la actualización de un controlador, simplemente no ha (re) instanciado la sesión en ese momento.

¿Alguien sabe si esto es por diseño y, de ser así, por qué?

[Me las arreglé para eludir el problema utilizando un patrón de carga diferida].

Chris Arnold
fuente

Respuestas:

79

Andrei tiene razón: es nulo porque cuando se ejecuta bajo el marco ASP.NET MVC, HttpContext (y por lo tanto HttpContext.Session) no se establece cuando la clase de controlador se construye como cabría esperar, pero se establece ("inyecta") más tarde por la clase ControllerBuilder. Si desea una mejor comprensión del ciclo de vida, puede desplegar el marco ASP.NET MVC (la fuente está disponible) o consultar: esta página

Si necesita acceder a la sesión, una forma sería anular el método "OnActionExecuting" y acceder allí, ya que estará disponible en ese momento.

Sin embargo, como sugiere Andrei, si su código depende de la sesión, entonces podría ser potencialmente difícil escribir pruebas unitarias, por lo que tal vez podría considerar envolver la sesión en una clase auxiliar que luego se puede cambiar por una diferente, no versión web cuando se ejecuta bajo pruebas unitarias, por lo tanto, desacopla su controlador de la web.

Andrew W
fuente
3
No estoy seguro de que esta sea una declaración adecuada sobre HttpContext. En realidad, se construyó justo al comienzo de todo el flujo. Puede leer un poco sobre el flujo detallado aquí beletsky.net/2011/06/inside-aspnet-mvc-route-to-mvchanlder.html o puede usar reflector y encontrarse cuando se ha creado una instancia de httpContext, es alrededor de la línea 1556 en httpruntime .cs.
Alexey Shcherbak
@AlexeyShcherbak Puede que ya esté construido: OP se trata de si se ha configurado en la propiedad Session del controlador MVC. es decir, sesión pública HttpSessionStateBase {get; } en System.Web.Mvc.Controller Estas son cosas diferentes.
MemeDeveloper
61

Además de las otras respuestas aquí, aunque Controller.Sessionno se completa en el constructor, aún puede acceder a la sesión a través de:

System.Web.HttpContext.Current.Session

con la salvedad estándar de que esto reduce potencialmente la capacidad de prueba de su controlador.

Mike Chamberlain
fuente
3
El tipo de cada una de estas dos propiedades de sesión es diferente, lo que puede ser importante si tiene la intención de mantener una referencia al estado de la sesión en sí.
BrianCooksey
@BrianCooksey ¿qué es diferente?
MichaelMao
1
Controller.Session es de tipo System.Web.HttpSessionStateBase (consulte msdn.microsoft.com/en-us/library/… ) pero System.Web.HttpContext.Current.Session es de tipo System.Web.SessionState.HttpSessionState (consulte msdn .microsoft.com / en-us / library /… )
BrianCooksey
Respuesta anterior, pero quería decir que System.Web.HttpContext.Current.Sessiontambién está nullen el instanciador MVC VS2019.
jp2code
11

La sesión se inyecta más adelante en el ciclo de vida. ¿Por qué necesitas la sesión en el constructor de todos modos? Si lo necesita para TDD, debe envolver la sesión en un objeto simulado.

Andrei Rînea
fuente
1
Para agregar a Andrei Rinea, este es un ejemplo específico de la técnica mencionada por él: iridescence.no/post/…
murki
4
Quiero acceder a la sesión durante mis constructores para poder tener acceso a la información de la sesión almacenada previamente. Sí, podría anular el método OnActionExecuting, pero esta ciertamente no es una solución elegante.
Chris Arnold
8

Puede anular el método Initialize para configurar su sesión.

protected override void Initialize(RequestContext requestContext)
Funlover
fuente
2

Si está utilizando un contenedor de IoC, intente inyectar y usar el en HttpSessionStateBaselugar del Sessionobjeto:

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

Esta respuesta puede ser útil para algunas personas.

Si anulamos el método Initialize, entonces tenemos que inicializar la clase base con el contexto de la solicitud: base.Initialize (requestContext);

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

        }
Prashanth vunnam gcs
fuente
Útil. Tenga en cuenta que el método signature protected override void Initialize(System.Web.Routing.RequestContext requestContext).
Martin_W