Sessão nula em construtores de controladores ASP.Net MVC

88

Por que a Session é nula nos construtores dos controladores? Ele pode ser acessado em Métodos de ação. Presumivelmente, como a estrutura de roteamento MVC é responsável pela atualização de um controlador, ela simplesmente não (re) instanciou a sessão naquele ponto.

Alguém sabe se isso é intencional e, em caso afirmativo, por quê?

[Consegui contornar o problema usando um padrão de carregamento lento.]

Chris Arnold
fonte

Respostas:

78

Andrei está certo - é nulo porque quando executado sob a estrutura ASP.NET MVC, o HttpContext (e, portanto, HttpContext.Session) não é definido quando a classe do controlador é construída como você poderia esperar, mas é definido ("injetado") mais tarde pela classe ControllerBuilder. Se você quiser um melhor entendimento do ciclo de vida, você pode puxar para baixo o framework ASP.NET MVC (o código-fonte está disponível) ou consultar: esta página

Se você precisar acessar a Sessão, uma forma seria sobrescrever o método "OnActionExecuting" e acessá-lo lá, pois ele estará disponível nessa hora.

No entanto, como Andrei está sugerindo, se seu código é dependente da Sessão, então pode ser potencialmente difícil escrever testes de unidade, então talvez você possa considerar envolver a Sessão em uma classe auxiliar que pode ser trocada por uma classe diferente, não versão web quando executado em testes de unidade, portanto, desacoplando seu controlador da web.

Andrew W
fonte
2
Não tenho certeza se esta é uma declaração apropriada sobre HttpContext. Na verdade, ele foi construído bem no início de todo o fluxo. Você pode ler um pouco sobre o fluxo detalhado aqui beletsky.net/2011/06/inside-aspnet-mvc-route-to-mvchanlder.html ou você pode usar o refletor e descobrir quando o httpContext foi instanciado - é em torno da linha 1556 em httpruntime .cs.
Alexey Shcherbak
@AlexeyShcherbak Ele já pode ser construído - OP trata se ele foi definido na propriedade Session do controlador MVC. isto é, pública HttpSessionStateBase Session {get; } em System.Web.Mvc.Controller São coisas diferentes.
MemeDeveloper
61

Além das outras respostas aqui, embora Controller.Sessionnão esteja preenchido no construtor, você ainda pode acessar a sessão por meio de:

System.Web.HttpContext.Current.Session

com a advertência padrão de que isso reduz potencialmente a testabilidade do seu controlador.

Mike Chamberlain
fonte
3
O tipo de cada uma dessas duas propriedades da sessão é diferente, o que pode ser importante se você pretende manter uma referência ao próprio estado da sessão.
BrianCooksey,
@BrianCooksey o que é diferente?
MichaelMao
1
Controller.Session é do tipo System.Web.HttpSessionStateBase (consulte msdn.microsoft.com/en-us/library/… ), mas System.Web.HttpContext.Current.Session é do tipo System.Web.SessionState.HttpSessionState (consulte msdn .microsoft.com / en-us / library /… )
BrianCooksey
10

A Sessão é injetada posteriormente no ciclo de vida. Por que você precisa da sessão no construtor de qualquer maneira? Se você precisar dele para o TDD, deve envolver a sessão em um objeto mockable.

Andrei Rînea
fonte
1
Para adicionar a Andrei Rinea, este é um exemplo específico da técnica por ele mencionada: iridescence.no/post/…
murki
4
Desejo acessar a Sessão durante meus construtores para que possa ter acesso às informações da sessão armazenadas anteriormente. Sim, eu poderia substituir o método OnActionExecuting, mas certamente não é uma solução elegante.
Chris Arnold
8

Você pode substituir o método Initialize para definir sua sessão.

protected override void Initialize(RequestContext requestContext)
Funlover
fonte
2

Se você estiver usando um contêiner IoC, tente injetar e usar o em HttpSessionStateBasevez do Sessionobjeto:

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

Esta resposta pode ser útil para algumas pessoas

Se substituirmos o método Initialize, teremos que inicializar a classe base com o contexto de solicitação: base.Initialize (requestContext);

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

        }
Prashanth vunnam gcs
fonte
Útil. Observe a assinatura do método protected override void Initialize(System.Web.Routing.RequestContext requestContext).
Martin_W