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

jopen 8年前

在项目中使用异步(async await)的时候发现一个现象,HttpContext.Current为null,导致一系列的问题 .

上网查了一些资料后找到了一个对象: System.Threading.SynchronizationContext (提供在各种同步模型中传播同步上下文的基本功能。),  跟踪代码后发现  SynchronizationContext.Current 返回的是一个叫  System.Web.LegacyAspNetSynchronizationContext 的内部类对象,这个对象中有个字段叫 _application,类型为  System.Web.HttpApplication.  有了  System.Web.HttpApplication  对象就好办多了,直接使用 Context 属性即可. (当然 SynchronizationContext.Current 不一定只可能返回  System.Web.LegacyAspNetSynchronizationContext ,具体没有研究了)

代码:

   class Test      {          void Test1()          {              SynchronizationContext context = SynchronizationContext.Current;              Delegate factory = null;              Type type = context.GetType();              if (!type.FullName.Equals("System.Web.LegacyAspNetSynchronizationContext"))              {                  //目前只研究了这个                  return;              }              //找到字段              ParameterExpression sourceExpression = Expression.Parameter(typeof(System.Threading.SynchronizationContext), "context");              //目前支持 System.Web.LegacyAspNetSynchronizationContext 内部类              //查找        private HttpApplication _application 字段               Expression sourceInstance = Expression.Convert(sourceExpression, type);              FieldInfo applicationFieldInfo = type.GetField("_application", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);              Expression fieldExpression = Expression.Field(sourceInstance, applicationFieldInfo);              factory = Expression.Lambda<Func<System.Threading.SynchronizationContext, System.Web.HttpApplication>>(fieldExpression, sourceExpression).Compile();                //拿到了这个委托就好办了                System.Web.HttpApplication httpApplication = ((Func<System.Threading.SynchronizationContext, System.Web.HttpApplication>)factory)(context);                //直接使用属性              System.Web.HttpContext httpContext = httpApplication.Context;          }      }

封装一下:

      /// <summary>          /// 在同步上下文中查找当前会话<see cref="System.Web.HttpContext" />对象          /// </summary>          /// <param name="context"></param>          /// <returns></returns>          public static HttpContext FindHttpContext(this SynchronizationContext context)          {              if (context == null)              {                  return null;              }              var factory = GetFindApplicationDelegate(context);              if (factory == null)              {                  return null;              }              return factory(context).Context;          }

接下来再为HttpContext对象添加一个System.Web命名空间的扩展方法:

        /// <summary>          /// 确定异步状态的上下文可用          /// </summary>          /// <param name="context"></param>          /// <returns></returns>          public static HttpContext Check(this HttpContext context)          {              if (context==null)              {                  context = SynchronizationContext.Current.FindHttpContext();              }              return context;          }

为了性能考虑,把factory缓存起来即可.

完了之后,使用HttpContext对象就可以这么做:

HttpContext context=HttpContext.Current.Check();    //这时候context还为null的话,就是姿势不对了(包括 websocket ,没有在会话中使用等等)

以后就可以愉快的使用异步控制器啦!

来自: http://www.cnblogs.com/pokemon/p/5116446.html