如何解决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.Current
和 SynchronizationContext.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;
}
}
成功调用栈:
使用空上下文调用堆栈:
更新
抓住稻草,但经过一段时间的思考后,似乎 EF 6 内部的某些东西可能正在以丢失 IDbConnectionInterceptor.Opened
的方式在线程上排队对 SynchronizationContext
的调用。因此,我在成功的堆栈跟踪之后查看了 EF 源代码,看起来对 Opened
的调用已启动 here in InternalDispatcher.DispatchAsync<TTarget,TInterceptionContext>
line 257。我不确定它如何解释我的问题的间歇性,但它可能与此处使用的 Task.ContinueWith
有关系吗?有趣的是,我发现 this other question 与 Task.ContinueWith
该方法和 SynchronizationContext
丢失有关。然后我发现了这个问题,答案说它将继续使用 ThreadPool
线程,除非明确指定,否则它将没有关联的 SyncrhonizationContext
线程。所以这听起来像是我要找的,但我不确定使用的 TaskContinuationoptions.ExecuteSynchronously
选项是否会改变任何东西,如果这是罪魁祸首,我还不明白为什么我的 HttpContext
大部分时间可用。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。