Нулевой сеанс в конструкторах контроллера MVC ASP.Net

88

Почему в конструкторах контроллеров Session null? Доступ к нему можно получить из методов действия. Предположительно, поскольку инфраструктура маршрутизации MVC отвечает за создание контроллера, она просто не (повторно) создала экземпляр сеанса в этот момент.

Кто-нибудь знает, задумано ли это и если да, то почему?

[Мне удалось обойти проблему, используя шаблон отложенной загрузки.]

Крис Арнольд
источник

Ответы:

79

Андрей прав - это null, потому что при работе в среде ASP.NET MVC HttpContext (и, следовательно, HttpContext.Session) не устанавливается, когда класс контроллера конструируется, как вы могли ожидать, но он устанавливается («внедряется») позже. классом ControllerBuilder. Если вы хотите лучше понять жизненный цикл, вы можете либо вытащить платформу ASP.NET MVC (источник доступен), либо обратиться к этой странице.

Если вам нужно получить доступ к сеансу, то одним из способов было бы переопределить метод «OnActionExecuting» и получить доступ к нему там, так как он будет доступен к тому времени.

Однако, как предлагает Андрей, если ваш код зависит от сеанса, то потенциально может быть сложно писать модульные тесты, поэтому, возможно, вы могли бы подумать о том, чтобы обернуть сеанс во вспомогательный класс, который затем можно заменить на другой, не веб-версия при запуске в модульных тестах, поэтому отключите ваш контроллер от Интернета.

Эндрю В
источник
3
Я не уверен, что это правильное заявление о HttpContext. Фактически он построен прямо в начале всего потока. Вы можете прочитать немного о подробном потоке здесь beletsky.net/2011/06/inside-aspnet-mvc-route-to-mvchanlder.html, или вы можете использовать отражатель и обнаружить себя, когда httpContext был создан - это примерно строка 1556 в httpruntime .cs.
Алексей Щербак
@AlexeyShcherbak Возможно, он уже построен - OP о том, был ли он установлен в свойстве Session контроллера MVC. т.е. общедоступный сеанс HttpSessionStateBase {получить; } в System.Web.Mvc.Controller. Это разные вещи.
MemeDeveloper
61

В дополнение к другим ответам здесь, хотя Controller.Sessionон не заполнен в конструкторе, вы все равно можете получить доступ к сеансу через:

System.Web.HttpContext.Current.Session

со стандартной оговоркой, что это потенциально снижает тестируемость вашего контроллера.

Майк Чемберлен
источник
3
Типы для каждого из этих двух свойств сеанса различаются, что может иметь значение, если вы намереваетесь сохранить ссылку на само состояние сеанса.
BrianCooksey
@BrianCooksey в чем разница?
MichaelMao
1
Controller.Session имеет тип System.Web.HttpSessionStateBase (см. Msdn.microsoft.com/en-us/library/… ), но System.Web.HttpContext.Current.Session имеет тип System.Web.SessionState.HttpSessionState (см. Msdn .microsoft.com / en-us / library /… )
Брайан Кукси,
Старый ответ, но хотел сказать, что System.Web.HttpContext.Current.Sessionэто также есть nullв инстанциаторе VS2019 MVC.
jp2code
11

Сессия вводится позже в жизненном цикле. Зачем вообще нужен сеанс в конструкторе? Если вам это нужно для TDD, вы должны обернуть сеанс в имитируемый объект.

Андрей Рыня
источник
1
Чтобы добавить к Андрею Ринеа, это конкретный пример упомянутой им техники: iridescence.no/post/…
murki
4
Я хочу получить доступ к сеансу во время своих конструкторов, чтобы иметь доступ к ранее сохраненной информации о сеансе. Да, я мог бы переопределить метод OnActionExecuting, но это определенно не изящное решение.
Крис Арнольд,
8

Вы можете переопределить метод Initialize, чтобы установить сеанс.

protected override void Initialize(RequestContext requestContext)
Funlover
источник
2

Если вы используете контейнер IoC, попробуйте ввести и использовать HttpSessionStateBaseвместо Sessionобъекта:

private static Container defaultContainer()
{
    return new Container(ioc =>
    {
        // session manager setup
        ioc.For<HttpSessionStateBase>()
           .Use(ctx => new HttpSessionStateWrapper(HttpContext.Current.Session)); 
    });
}
ВахидН
источник
2

Этот ответ может быть полезен для некоторых людей

Если мы переопределим метод Initialize, мы должны инициализировать базовый класс контекстом запроса: base.Initialize (requestContext);

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

        }
Prashanth vunnam gcs
источник
Полезно. Обратите внимание, что подпись метода protected override void Initialize(System.Web.Routing.RequestContext requestContext).
Martin_W