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

Posted 腾信@迪克猪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了异步 HttpContext.Current 为空null 另一种解决方法相关的知识,希望对你有一定的参考价值。

1、场景

    在导入通讯录过程中,把导入的失败、成功的号码数进行统计,然后保存到session中,客户端通过轮询显示状态。

    在实现过程中,使用的async调用方法,出现HttpContext.Current为null的情况,如下:

image

 

2、网络解答

    从百度与谷歌查询,分以下两种情况进行解答:

1、更改web.config配置文件

    Stackoverflow给出如下解决方案:http://stackoverflow.com/questions/18383923/why-is-httpcontext-current-null-after-await

    image

2、缓存HttpContext

    博客地址:http://www.cnblogs.com/pokemon/p/5116446.html

    本博客,给出了异步下HttpContext.Current为空的原因分析。本文中的最后提取出如下方法:

using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Web;

namespace TxSms
{
    /// <summary>
    /// 解决Asp.net Mvc中使用异步的时候HttpContext.Current为null的方法
    /// <remarks>
    /// http://www.cnblogs.com/pokemon/p/5116446.html
    /// </remarks>
    /// </summary>
    public static class HttpContextHelper
    {
        /// <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);
            return factory?.Invoke(context).Context;
        }

        private static Func<SynchronizationContext, HttpApplication> GetFindApplicationDelegate(SynchronizationContext context)
        {
            Delegate factory = null;
            Type type = context.GetType();
            if (!type.FullName.Equals("System.Web.LegacyAspNetSynchronizationContext"))
            {
                return null;
            }
            //找到字段
            ParameterExpression sourceExpression = Expression.Parameter(typeof(SynchronizationContext), "context");
            //目前支持 System.Web.LegacyAspNetSynchronizationContext 内部类
            //查找        private HttpApplication _application 字段
            Expression sourceInstance = Expression.Convert(sourceExpression, type);
            FieldInfo applicationFieldInfo = type.GetField("_application", BindingFlags.NonPublic | BindingFlags.Instance);
            Expression fieldExpression = Expression.Field(sourceInstance, applicationFieldInfo);
            factory = Expression.Lambda<Func<SynchronizationContext, HttpApplication>>(fieldExpression, sourceExpression).Compile();

            //返回委托
            return ((Func<SynchronizationContext, HttpApplication>)factory);
        }

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

 

3、实现

    通过2对两种方法都进行尝试,发现还是不可用的。使用session共享数据行不通,换另外一种思路,使用cache,封装类库如下:

using System;
using System.Collections;
using System.Web;
using System.Web.Caching;

namespace TxSms
{
    /// <summary>
    /// HttpRuntime Cache读取设置缓存信息封装
    /// <auther>
    ///     <name>Kencery</name>
    ///     <date>2015-8-11</date>
    /// </auther>
    /// 使用描述:给缓存赋值使用HttpRuntimeCache.Set(key,value....)等参数(第三个参数可以传递文件的路径(HttpContext.Current.Server.MapPath()))
    /// 读取缓存中的值使用JObject jObject=HttpRuntimeCache.Get(key) as JObject,读取到值之后就可以进行一系列判断
    /// </summary>
    public static class HttpRuntimeCache
    {
        /// <summary>
        /// 设置缓存时间,配置(从配置文件中读取)
        /// </summary>
        private const double Seconds = 30 * 24 * 60 * 60;

        /// <summary>
        /// 缓存指定对象,设置缓存
        /// </summary>
        public static bool Set(string key, object value)
        {
            return Set(key, value, null, DateTime.Now.AddSeconds(Seconds), Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
        }

        /// <summary>
        ///  缓存指定对象,设置缓存
        /// </summary>
        public static bool Set(string key, object value, string path)
        {
            try
            {
                var cacheDependency = new CacheDependency(path);
                return Set(key, value, cacheDependency);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 缓存指定对象,设置缓存
        /// </summary>
        public static bool Set(string key, object value, CacheDependency cacheDependency)
        {
            return Set(key, value, cacheDependency, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
        }

        /// <summary>
        /// 缓存指定对象,设置缓存
        /// </summary>
        public static bool Set(string key, object value, double seconds, bool isAbsulute)
        {
            return Set(key, value, null, (isAbsulute ? DateTime.Now.AddSeconds(seconds) : Cache.NoAbsoluteExpiration),
                (isAbsulute ? Cache.NoSlidingExpiration : TimeSpan.FromSeconds(seconds)), CacheItemPriority.Default, null);
        }

        /// <summary>
        /// 获取缓存对象
        /// </summary>
        public static object Get(string key)
        {
            return GetPrivate(key);
        }

        /// <summary>
        /// 判断缓存中是否含有缓存该键
        /// </summary>
        public static bool Exists(string key)
        {
            return (GetPrivate(key) != null);
        }

        /// <summary>
        /// 移除缓存对象
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool Remove(string key)
        {
            if (string.IsNullOrEmpty(key))
            {
                return false;
            }
            HttpRuntime.Cache.Remove(key);
            return true;
        }

        /// <summary>
        /// 移除所有缓存
        /// </summary>
        /// <returns></returns>
        public static bool RemoveAll()
        {
            IDictionaryEnumerator iDictionaryEnumerator = HttpRuntime.Cache.GetEnumerator();
            while (iDictionaryEnumerator.MoveNext())
            {
                HttpRuntime.Cache.Remove(Convert.ToString(iDictionaryEnumerator.Key));
            }
            return true;
        }

        /// <summary>
        /// 设置缓存
        /// </summary>
        public static bool Set(string key, object value, CacheDependency cacheDependency, DateTime dateTime,
            TimeSpan timeSpan, CacheItemPriority cacheItemPriority, CacheItemRemovedCallback cacheItemRemovedCallback)
        {
            if (string.IsNullOrEmpty(key) || value == null)
            {
                return false;
            }
            HttpRuntime.Cache.Insert(key, value, cacheDependency, dateTime, timeSpan, cacheItemPriority, cacheItemRemovedCallback);
            return true;
        }

        /// <summary>
        /// 获取缓存
        /// </summary>
        private static object GetPrivate(string key)
        {
            return string.IsNullOrEmpty(key) ? null : HttpRuntime.Cache.Get(key);
        }
    }
}

    在此调试,完全可以找到((ImportContactStateModel)model).SuccessNum,如下图:

image

以上是关于异步 HttpContext.Current 为空null 另一种解决方法的主要内容,如果未能解决你的问题,请参考以下文章

路由请求时 HttpContext.Current.Session 为空

使用 IAuthenticationFilter 时 HttpContext.Current 为空

HttpContext.Current.User.Identity.Name 为空

HttpContext.Current.User.Identity.Name 为空

HttpContext.Current 在 TokenCache.BeforeAccess 上为空

ServiceStack - System.Web.HttpContext.Current.Session 为空