ASP.Net MVC控制器构造函数中的会话null


88

为什么在Controllers的构造函数中Session为null?可以从Action方法中访问它。据推测,由于MVC路由框架负责更新Controller,因此它当时还没有(重新)实例化Session。

有谁知道这是否是设计使然,为什么?

[我已经设法通过使用惰性加载模式来解决此问题。]

Answers:


79

Andrei是正确的-它为null,因为在ASP.NET MVC框架下运行时,未按预期构造控制器类时会设置HttpContext(因此是HttpContext.Session),但稍后会设置(“注入”)通过ControllerBuilder类。如果您想更好地了解生命周期,可以下拉ASP.NET MVC框架(源可用),或参考:此页面

如果您需要访问Session,则一种方法是重写“ OnActionExecuting”方法并在那里进行访问,因为届时它将可用。

但是,正如Andrei所建议的那样,如果您的代码依赖于Session,则可能难以编写单元测试,因此也许您可以考虑将Session封装在一个帮助器类中,然后可以将其换成其他非在单元测试下运行时为Web版本,因此将控制器与Web分离。


3
我不确定这是否是关于HttpContext的正确声明。它实际上是在整个流程开始时构建的。您可以在此处阅读有关详细流程的一些信息beletsky.net/2011/06/inside-aspnet-mvc-route-to-mvchanlder.html ,也可以使用反射器,并在实例化httpContext时发现自己-在httpruntime的1556行附近.cs。
阿列克谢·谢尔巴克

@AlexeyShcherbak可能已经构造好-OP的问题在于它是否已在MVC控制器的Session属性上设置。即公开HttpSessionStateBase会话{ 在System.Web.Mvc.Controller上,这些是不同的事情。
MemeDeveloper

61

除了此处的其他答案外,虽然Controller.Session未在构造函数中填充,但您仍可以通过以下方式访问会话:

System.Web.HttpContext.Current.Session

标准警告,这可能会降低控制器的可测试性。


3
这两个会话属性各自的类型不同,如果要保留对会话状态本身的引用,这可能很重要。
BrianCooksey 2013年

@BrianCooksey有什么不同?
MichaelMao

1
Controller.Session的类型为System.Web.HttpSessionStateBase(请参阅msdn.microsoft.com/zh-cn/library/…),但是System.Web.HttpContext.Current.Session的类型为System.Web.SessionState.HttpSessionState(请参见msdn .microsoft.com / en-us / library /…
BrianCooksey 2016年

旧答案,但想说System.Web.HttpContext.Current.Session的也是nullVS2019 MVC实例。
jp2code

11

该会话将在生命周期的后期注入。为什么仍然需要在构造函数中使用会话?如果需要将其用于TDD,则应将会话包装到一个可模拟的对象中。


1
除了Andrei Rinea之外,这是他提到的技术的一个具体示例:iridescence.no/post/…–
murki

4
我想在构造函数期间访问Session,以便可以访问以前存储的Session信息。是的,我可以重写OnActionExecuting方法,但这当然不是一个很好的解决方案。
克里斯·阿诺德

8

您可以覆盖Initialize方法来设置会话。

protected override void Initialize(RequestContext requestContext)

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);
           

        }

有用。注意方法签名protected override void Initialize(System.Web.Routing.RequestContext requestContext)
Martin_W
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.