Это странный случай, с которым я столкнулся в каком-то устаревшем коде, который я пометил для перефакторинга.Я проверил это, и код не использует .ConfigureAwait(false)
..
Данный код выглядит примерно так: (Строки "testx = ...." являются частью отладки проблемы дораскрыть поведение.)
public async Task<ActionResult> Index()
{
ValidateRoleAccess(Roles.Admin, Roles.AuthorizedUser, Roles.AuditReadOnly);
var test1 = System.Web.HttpContext.Current != null
var decisions = await _lookupService.GetAllDecisions();
var test2 = System.Web.HttpContext.Current != null
var statuses = await _lookupService.GetAllEnquiryStatuses();
var test3 = System.Web.HttpContext.Current != null
var eeoGroups = await _lookupService.GetEEOGroups();
var test4 = System.Web.HttpContext.Current != null
var subCategories = await _lookupService.GetEnquiryTypeSubCategories();
var test5 = System.Web.HttpContext.Current != null
var paystreams = await _lookupService.GetPaystreams();
var test6 = System.Web.HttpContext.Current != null
var hhses = await _lookupService.GetAllHHS();
var test7 = System.Web.HttpContext.Current != null
// ...
Сами вызовы - это простые запросы к EF через одну и ту же службу поиска.Учитывая, что они являются EF и используют один и тот же UoW / DbContext, их нельзя изменить на использование Task.WhenAll()
.
Ожидаемые результаты: верно для test1 -> test7
Фактические результаты: верно для test1, -> test3, False для test4 -> test7
Проблема была обнаружена, когда я добавил проверку для определенной роли после ожидаемых вызовов поиска.При проверке возникла исключительная ситуация пустой ссылки на HttpContext.Current, которую использует метод проверки.Таким образом, он прошел при использовании в вызове ValidateRoleAccess перед асинхронным вызовом, но не прошел, если был вызван после всех ожидаемых методов.
Я изменил порядок методов и завершился неудачно после 2 или 3 ожиданий, без особого виновника.методы.Приложение ориентировано на .Net 4.6.1.Это неблокирующая проблема, так как я смог выполнить проверку роли до ожидания, поместить результат в переменную и ссылаться на переменную после ожидания, но это была очень неожиданная «работа» после 1-2 ждет, но не более.Код будет пересматриваться, так как асинхронные вызовы не нужны для этих поисков, они не возвращают целые сущности, но мне все еще очень любопытно, если есть объяснение, почему HttpContext будет «потерян» после парыожидаемых задач с .ConfigureAwait (false) не использовалось.
Обновление 1: график утолщается. Я настроил вызовы теста, чтобы добавить результаты теста Boolean в список, затем повторил набор нагрузок для 5 итераций,Я хотел посмотреть, не достигнет ли он когда-нибудь значения «Ложь», и не вернется ли когда-нибудь к значению «Истина».Я думаю, что ожидание возобновлялось в другом потоке, который не имел ссылки на текущий HttpContext, однако, независимо от того, сколько итераций я добавил, однажды ложный, он всегда был ложным.Итак, затем я попытался повторить первый вызов (GetAllDecisions) 10 раз ... Удивительно, но все первые 10 итераций вернулись как True ?!Поэтому я более внимательно посмотрел на изменение порядка, чтобы увидеть, были ли вызовы, которые надежно сработали, и оказалось, что их было 3.
, например:
var decisions = await _lookupService.GetAllDecisions();
results.Add(System.Web.HttpContext.Current != null);
decisions = await _lookupService.GetAllDecisions();
results.Add(System.Web.HttpContext.Current != null);
decisions = await _lookupService.GetAllDecisions();
results.Add(System.Web.HttpContext.Current != null);
вернул True, True, True, но затем изменил это на:
var eeoGroups = _lookupService.GetEEOGroups();
results.Add(System.Web.HttpContext.Current != null);
eeoGroups = _lookupService.GetEEOGroups();
results.Add(System.Web.HttpContext.Current != null);
eeoGroups = _lookupService.GetEEOGroups();
results.Add(System.Web.HttpContext.Current != null);
вернул False, False, False.
Копая немного глубже, я заметил, что методы были смесьюEntityFramework и более старый код репозитория на основе NHibernate.Это были асинхронные методы EntityFramework, которые вызывали контекст в ожидании.
Один из методов, который отключал контекст после ожидания:
public async Task<List<string>> GetEEOGroups()
{
return await _dbContext.EmployeeEEOGroup.GroupBy(e => e.EEOGroup).Select(g => g.FirstOrDefault().EEOGroup).ToListAsync();
}
, как это было: * edit -whupsэто была копия / вставка дубликата:)
public async Task<IEnumerable<SapHHS>> GetAllHHS()
{
return await _dbContext.HHS.Where(x => x.IsActive).ToListAsync();
}
Но это было прекрасно:
public async Task<IEnumerable<Decision>> GetAllDecisions()
{
return await Task.FromResult(_repository.Session.QueryOver<Lookup>().Where(l => l.Type == "Decision" && l.IsActive).List().Select(l => new Decision { DecisionId = l.Id, Description = l.Name }).ToList());
}
Глядя на код, который «работает», довольно ясно, что на самом деле он ничего не делает Asyncучитывая Task.FromResult против синхронного метода.Я думаю, что оригинальные авторы были пойманы в очарование асинхронной серебряной пули и просто обернули старый код для согласованности.Асинхронные методы EF работают с await, но там, где кажется, что async / await поддерживается с HttpContext.Current, пока <httpRuntime targetFramework="4.5" />
, EF, похоже, опровергает это предположение.