在MVC中,HttpContext.Current是比较常见的对象,可以用它来进行Session,Cache等的保存等。但是它并不是无处不在的,下面就慢慢来揭开它的面纱。

当我们向服务端发送请求的时候,页面会响应我们的请求,比如我们访问A页面,那么服务端就会把A页面通过处理后返回给我们,访问B页面,过程同样。在这里,我们访问A页面和访问B页面,总共进行了2次请求,这两次请求会不会是在同一个页面线程中呢?因为Asp.net模型本身就是多线程模式的,那么让我们来做一个实验:

首先,创建一个Asp.net项目,新建一个Default.aspx主页面,然后新建一个About.aspx从页面,在Default.aspx页面中,代码如下:

   1:   protected void Page_Load(object sender, EventArgs e)
   2:   {
   3:        Response.Write("Default页面线程ID:" + Thread.CurrentThread.ManagedThreadId);
   4:        HttpContext.Current.Cache.Insert("test", "TestValue", null, DateTime.Now.AddMinutes(120),System.Web.Caching.Cache.NoSlidingExpiration);
   5:   }

在代码中,我们打印出了当前页面的线程ID并做了一个简单的缓存.

在About.aspx页面中,代码如下:

   1:   protected void Page_Load(object sender, EventArgs e)
   2:   {
   3:       bool flag = HttpContext.Current == null;
   4:       Response.Write("About页面线程ID:" + Thread.CurrentThread.ManagedThreadId.ToString(+"<br/>");
   5:       Response.Write("About页面的Httpcontext对象:"+(flag?"未创建":"已经创建")+"<br/>");
   6:       Response.Write("About页面获取的缓存值为:"+HttpContext.Current.Cache["test"]+"<br/>");
   7:  }

在代码中,我们同样打印出了当前页面的线程ID并获取之前的缓存:

得到的结果如下:

   1:  Default页面线程ID:4
 
   3:  About页面线程ID:8
   4:  About页面的Httpcontext对象:已经创建
   5:  About页面获取的缓存值为:TestValue

从代码输出结果,我们可以看出,两次请求分别被不同的线程处理,HttpContext.Current在这两个线程中是可用的,为什么跨线程后还能够使用呢?这个后面我们再来讲解。

下面再看两个例子,在APM模式处理中和子线程处理中,看看HttpContext.Current是否依然能够访问:

首先是APM模式的例子:

   1:  private void DO()
   2:  {
   3:        System.Threading.Thread.Sleep(1000);
   4:        Response.Write(HttpContext.Current==null?"没有创建":"已经创建");
   5:  }
   6:   
   7:  private void BeginDO()
   8:  {
   9:        Action action = new Action(DO);
  10:        action.BeginInvoke((iar) =>
  11:        {
  12:            Action endAction = (Action)iar.AsyncState;
  13:             endAction.EndInvoke(iar);
  14:        }, action);
  15:  }

做好之后,让我们运行起来,结果在执行

Response.Write(HttpContext.Current==null?"没有创建":"已经创建");

这句话的时候报错,提示:响应在此上下文中不可用。看来,在APM中,是不可用的。

那么在子线程中呢?

   1:  private void ThreadDo()
   2:  {
   3:       new Thread(new ThreadStart(DO)).Start();
   4:  }

让我们执行,看看结果,不幸的是,当执行到同样那句话的时候报错,提示:响应在此上下文中不可用。

为什么在页面中,跨线程可用,但是在APM模式或者是创建了子线程的场景中不能用呢?

就让我们来剖析一下源代码:

首先,HttpContext.Current源码:

   1:  // System.Web.HttpContext
   2:  /// <summary>Gets or sets the <see cref="T:System.Web.HttpContext" /> object for the current HTTP request.</summary>
   3:  /// <returns>The <see cref="T:System.Web.HttpContext" /> for the current HTTP request.</returns>
   4:  public static HttpContext Current
   5:  {
   6:     get
   7:     {
   8:        return ContextBase.Current as HttpContext;
   9:     }
  10:     set
  11:     {
  12:        ContextBase.Current = value;
  13:     }
  14:  }

我们看到,是通过ContextBase.Current返回,让我们继续深究下去:

   1:  // System.Web.Hosting.ContextBase
   2:  internal static object Current
   3:  {    
   4:     get    
   5:     {        
   6:        return CallContext.HostContext;    
   7:     }    
   8:     [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]    
   9:     set    
  10:     {        
  11:        CallContext.HostContext = value;    
  12:     }
  13:  }

走到这里我们看到,是通过CallContext.HostContext对象返回,继续深究:

   1:  // System.Runtime.Remoting.Messaging.CallContext
   2:  /// <summary>Gets or sets the host context associated with the current thread.</summary>
   3:  /// <returns>The host context associated with the current thread.</returns>
   4:  /// <exception cref="T:System.Security.SecurityException">The immediate caller does not have infrastructure permission. </exception>
   5:  public static object HostContext
   6:  {
   7:      [SecurityCritical]
   8:      get
   9:      {
  10:          IllogicalCallContext illogicalCallContext = Thread.CurrentThread.GetIllogicalCallContext();
  11:          object hostContext = illogicalCallContext.HostContext;
  12:          if (hostContext == null)
  13:          {
  14:              LogicalCallContext logicalCallContext = CallContext.GetLogicalCallContext();
  15:              hostContext = logicalCallContext.HostContext;
  16:          }
  17:          return hostContext;
  18:      }
  19:      [SecurityCritical]
  20:      set
  21:      {
  22:          if (value is ILogicalThreadAffinative)
  23:          {
  24:              IllogicalCallContext illogicalCallContext = Thread.CurrentThread.GetIllogicalCallContext();
  25:              illogicalCallContext.HostContext = null;
  26:              LogicalCallContext logicalCallContext = CallContext.GetLogicalCallContext();
  27:              logicalCallContext.HostContext = value;
  28:              return;
  29:          }
  30:          LogicalCallContext logicalCallContext2 = CallContext.GetLogicalCallContext();
  31:          logicalCallContext2.HostContext = null;
  32:          IllogicalCallContext illogicalCallContext2 = Thread.CurrentThread.GetIllogicalCallContext();
  33:          illogicalCallContext2.HostContext = value;
  34:      }
  35:  }

看到了什么?从字面意思,我们可以看到:如果当前线程中存在与之相关的对象,则直接返回,否则就创建一个关联的对象并返回。

那么,也就是说,虽然请求的页面的线程变了,但是由于HttpContext.Current被当做相关对象被创建,所以可以在不同的页面中获取其实例。

但是为什么在APM模式或者子线程场景中不能访问呢?由于APM模式或者子线程场景的处理是被操作系统所接管,因为apm模式需要操作系统分配线程来进行异步处理,子线程也是由操作系统调配,这些与当前的请求已经没有关系,所以会出现无响应的情况。下面一幅图代表我对这个的理解:

另外,引用一下Fish-li的文章,Httpcontext.current在如下场景中是不可用的,我们看到,这些场景操作都是被操作系统所接管:

1. 定时器的回调。
2. Cache的移除通知。
3. APM模式下异步完成回调。
4. 主动创建线程或者将任务交给线程池来执行。

最后,有人说,那我可以把HttpContext.current对象全局缓存,然后放到子线程中使用可以么?我只能说,仍然不可以,仍然会出现“响应在此上下文中不可用”的错误提示。你不信可以试试看。

由于HttpContext.Current对象会被关联创建,所以,全局缓存他是没必要的。

我所知道的HttpContext.Current的更多相关文章

  1. HttpContext.Current:异步模式下的疑似陷阱之源

    最近园子里首页有好几篇文章都是讲异步编程的,尤其是几篇讲博客园自身的异步化建设的文章,看了以后很有收获. 闲暇之余再重新查查资料温故知新学习一遍,重新认识了SynchronizationContext ...

  2. 异步 HttpContext.Current 为空null 另一种解决方法

    1.场景 在导入通讯录过程中,把导入的失败.成功的号码数进行统计,然后保存到session中,客户端通过轮询显示状态. 在实现过程中,使用的async调用方法,出现HttpContext.Curren ...

  3. 解决Asp.net Mvc中使用异步的时候HttpContext.Current为null的方法

    在项目中使用异步(async await)的时候发现一个现象,HttpContext.Current为null,导致一系列的问题. 上网查了一些资料后找到了一个对象: System.Threading ...

  4. System.Web.HttpContext.Current.Session为NULL解决方法

    http://www.cnblogs.com/tianguook/archive/2010/09/27/1836988.html 自定义 HTTP 处理程序,从IHttpHandler继承,在写Sys ...

  5. Why is HttpContext.Current null after await?

    今天在对项目代码进行异步化改进的时候,遇到一个奇怪的问题(莫笑,以前没遇过),正如标题一样,HttpContext.Current 在 await 异步执行之后,就会变为 null. 演示代码: pu ...

  6. 在ASP.NET Core中怎么使用HttpContext.Current

    一.前言 我们都知道,ASP.NET Core作为最新的框架,在MVC5和ASP.NET WebForm的基础上做了大量的重构.如果我们想使用以前版本中的HttpContext.Current的话,目 ...

  7. HttpContext.Current.Session.SessionID相关问题及备忘

    今天Tony提到说我们系统中会利用如下代码来判断用户是否过期. if (string.IsNullOrEmpty(UserContext.ConnectionSessionId)) { LogUIFa ...

  8. 【C#】关于HttpContext.Current.Request.QueryString 你要知道点

    HttpContext.Current.Request.QueryString[ ]括号中是获取另一个页面传过的的参数值 HttpContext.Current.Request.Form[“ID”]· ...

  9. HttpContext.Current.User is null after installing .NET Framework 4.5

    故障原因:从framework4.0到framework4.5的升级过程中,原有的form认证方式发生了变化,所以不再支持User.Identity.Name原有存储模式(基于cookie),要恢复这 ...

随机推荐

  1. 【原】iOSCoreAnimation动画系列教程(二):CAKeyFrameAnimation【包会】

    在上一篇专题文章[原]iOSCoreAnimation动画系列教程(一):CABasicAnimation[包会]中我们学习了iOS核心动画CoreAnimation中CABasicAnimation ...

  2. js 毫秒 转 时间 日期 yyyy-mm-dd hh-mm-ss

    //格式化时间 var format = function(time, format){ var t = new Date(time); var tf = function(i){return (i ...

  3. Effective Java 29 Consider typesafe heterogeneous containers

    When a class literal is passed among methods to communicate both compile-time and runtime type infor ...

  4. 关于Redis中的数据类型

    一. Redis常用数据类型 Redis最为常用的数据类型主要有以下: String Hash List Set Sorted set 一张图说明问题的本质 图一: 图二: 代码: /* Object ...

  5. 问题解决——VS2010 将生成的文件复制到指定位置

    我是从VC6直接过渡到VS2010的,VS2008没怎么用过.用VS2010的时候,每次生成dll后,手工把dll.lib..h文件复制到指定文件夹太麻烦了,所以着手写了这个. =========== ...

  6. PHP Cannot redeclare class CLassName

    可能导致Cannot redeclare class CLassName错误的原因: 1.在同一个文件中重复声明了两次同名的类: class Extend {} class Extend {} new ...

  7. [转]Worksheet.Change Event (Excel)

    本文转自:https://msdn.microsoft.com/en-us/library/office/ff839775.aspx#AboutContributor Example   The fo ...

  8. WEB安全--Google Hacking

    通常我们用Google查询一些我们测试站点的一些信息,Google提供了一系列的搜索语句,下面我为大家详细的介绍一下! 常用语法: site:指定域名 intext:正文中存在关键字的网页 intit ...

  9. linux下安装+配置+卸载jdk

    一. 解压安装jdk在shell终端下进入jdk1.7.0_55.bin文件所在目录, 执行命令 ./jdk1.7.0_55.bin 这时会出现一段协议,连继敲回车,当询问是否同意的时候,输入yes, ...

  10. jquery模拟下拉框单选框复选Select,Checkbox,Radio

    在项目中,你会发现设计稿中常常会有单选框,复选框,但都不是系统默认的样式,这就可以用jquery来模拟它们:如图所示,实现它们所需要的代码如下: 首先需要引入的代码: <link rel=&qu ...