HttpContext.Current 在异步实体框架 6 查询期间有时会丢失为什么调用堆栈之间存在差异?

问题描述

我正在调试一些代码,并注意到偶尔,当它访问 HttpContext.Current 时,它为 null 并求助于用于处理该问题的回退。该代码一个异步 Web API 方法,它通过应用程序层向下调用并最终执行异步 Entity Framework 6 查询。 (下面的代码)除了 await [方法调用] - 没有 .ConfigureAwait(false) 或其他任何东西。

该项目有一个 System.Data.Entity.Infrastructure.IDbConnectionInterceptor,它在 Opened 方法中设置 sql 会话(用于 sql RLS)。它使用注入的依赖项,在本例中,它从 HttpContext.Current.Items 集合中获取所需的 ID。当我调试时,每次都有 95% 的时间它都可以工作,但偶尔我发现 HttpContext.CurrentSynchronizationContext.Current 都为空。

查看“调用堆栈”窗口,我可以看到它们以不同的方式到达 IDbConnectionInterceptor.Opened 方法。成功的版本返回到我在 Web API 控制器中的调用代码,但它为空的版本返回到本机代码。我想也许当它不为空时,它甚至不会在不同的线程上执行,但是 Open 确实在两种情况下都在与原始线程不同的线程上执行。我的项目面向 .NET Framework 4.8 并引用 Microsoft.AspNet.WebApi v5.2.3 nuget 包。它在配置文件中的 <httpRuntime targetFramework="4.7.2" /> 下有 <system.web>(我刚刚注意到它与框架的 4.8 不匹配)。我的理解是,从 .NET Framework 4.5 开始,上下文应该在异步调用之间流动,因此似乎有什么东西在阻止这种情况发生,或者 Opened 以某种方式在不使用异步/等待模型的线程上排队。那么有人可以帮助我理解失败请求的调用堆栈,为什么它可能与成功的请求不同,希望这可以如何解释缺失的上下文?

Web API 方法

    [HttpGet]
    [Infrastructure.Filters.AjaxOnly]
    [Route("event/month/list/{year}")]
    public async Task<IHttpActionResult> GetRoster___EventMonthItems(int year)
    {
        try
        {
            HttpContext.Current.SetCallContextFacilityID();    //This extension method sets the mentioned fallback value for when HttpContext.Current is null
            List<RosterDayListItem> data = await _roster___Mapper.GetRoster___EventMonthItems(year);
            return Ok(data);
        }
        catch (Exception ex)
        {
            Logging.DefaultLogger.Error(ex,"An error occurred while loading report categories.");
            return BadRequest(ex.Message);
        }
    }

EF6 查询

    public async Task<List<Roster___EventListItem>> GetRoster___EventListItems(int year,int month)
    {
        using (var dbContextScope = _dbContextFactory.Create())
        {
            var context = GetContext(dbContextScope);

            var result = await context.DropInEvents
                .Where(w => w.EventDate.Year == year && w.EventDate.Month == month && w.IsDeleted == false)
                .Select(d => new Roster___EventListItem
                {
                    ID = d.ID,EventDate = d.EventDate,EventTime = d.StartTime,Year = d.EventDate.Year
                })
                .OrderBy(f => f.EventDate).ThenBy(f => f.EventTime)
                .ThenByDescending(f => f.EventDate)
                .ToListAsync();

            return result;
        }
    }

成功调用栈:

Successful Call Stack

使用空上下文调用堆栈:

Call Stack with null contexts


更新

抓住稻草,但经过一段时间的思考后,似乎 EF 6 内部的某些东西可能正在以丢失 IDbConnectionInterceptor.Opened 的方式在线程上排队对 SynchronizationContext调用。因此,我在成功的堆栈跟踪之后查看了 EF 源代码,看起来对 Opened调用已启动 here in InternalDispatcher.DispatchAsync<TTarget,TInterceptionContext> line 257。我不确定它如何解释我的问题的间歇性,但它可能与此处使用的 Task.ContinueWith 有关系吗?有趣的是,我发现 this other questionTask.ContinueWith方法SynchronizationContext 丢失有关。然后我发现了这个问题,答案说它将继续使用 ThreadPool 线程,除非明确指定,否则它将没有关联的 SyncrhonizationContext 线程。所以这听起来像是我要找的,但我不确定使用的 TaskContinuationoptions.ExecuteSynchronously 选项是否会改变任何东西,如果这是罪魁祸首,我还不明白为什么我的 HttpContext 大部分时间可用。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)