在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. iOS之UI--自定义IOS的HYCheckBox源码的使用

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  2. iOS设计模式 - 单例

    备注:只能通过类的类方法才能创建单例类的实例,[[类名 alloc]init]创建实例没有用的. 原理图 说明 1. 单例模式人人用过,严格的单例模式很少有人用过 2. 严格的单例模式指的是无法通过常 ...

  3. 既生瑜何生亮?ASP.NET MVC VS ASP.NET Web API

    Asp.net MVC 与 Asp.net Web API 区别 在我们开发一些web应用时,我们一样可以在MVC Framework 中使用JsonResult 来返回JSON数据,同样也可以处理一 ...

  4. Mongodb在Windows上的配置

    1.打开mongodb的官网:https://www.mongodb.org/进行下载相应平台的安装包 2.我们选择最新版的3.2版本来下载,选择对应的操作系统版本来下载,这里选择windows Mo ...

  5. 有用的.NET库

    1. Polly,重试 Polly is a .NET 3.5 / 4.0 / 4.5 / PCL library that allows developers to express transien ...

  6. Effective Java 23 Don't use raw types in new code

    Generic types advantage Parameterized type can provide erroneous check in compile time. // Parameter ...

  7. jQuery Validate 表单验证插件----自定义校验结果样式

    一.下载依赖包 网盘下载:https://yunpan.cn/cryvgGGAQ3DSW  访问密码 f224 二.引入依赖包 <script src="../../scripts/j ...

  8. 烂泥:LVM学习之KVM利用LVM快照备份与恢复虚拟机

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. 最近一段时间一直在学习有关LVM逻辑卷方面的知识,前几篇文章介绍了有关LVM的逻辑卷的基本相关知识,包括逻辑卷及卷组的扩容与缩小.今天我们再来介绍LVM ...

  9. rabbitmq server的安装以及常用的命令

    Centos 源代码编译 安装 ErlangErlang依赖哪些库? A fully working GCC compiler environment         Ncurses developm ...

  10. luluzero的angularJs学习之路_angularJs示例代码

    最近开始自学 angularJs这个前端MVC框架,感觉在前端实现MVC很酷有木有.哈哈哈... 先说说我对前端MVC的一个基本的理解吧(刚开始学习接触得还比较浅显,理解可能会有些不到位,还请各位大神 ...