Взаимная блокировка асинхронного вызова в приложении MVC

Мы хотим отображать сведения о версии из наших серверных служб внизу всех страниц.

3 бэкэнд-сервиса — это асинхронные вызовы webapi(2.2), нам нужно, чтобы эти 3 асинхронных вызова объединяли дату в модель, а затем передавали ее в представление.

Если я делаю это как часть действия контроллера (не дочернего), код работает и отображает данные.

Однако попытка отобразить эти данные внизу каждой страницы (дочернее действие) приводит к нескольким различным условиям типа асинхронной взаимоблокировки.

дочернее действие и вызов _layout.cshtml

    //ControllerAction
    [ChildActionOnly]
    public ActionResult VersionInfo()
    {
        var version = _versionInfoViewModelMapper.Map().Result;
        return View(version);...
    }

//Layout.cshtml
    @if(Request.IsAuthenticated)
    {
        Html.RenderAction("VersionInfo", "Utils");
    }

Это приводит к тому, что кажется тупиком, поскольку запрос не заканчивается,

использование ActionFilter

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        var viewBag = filterContext.Controller.ViewBag;
        if (filterContext.RequestContext.HttpContext.Request.IsAuthenticated)
        {
            //viewBag.VersionInfo = Task.Run(() => _versionInfoViewModelMapper.Map()).Result;
            viewBag.VersionInfo = _versionInfoViewModelMapper.Map().Result;
        }
    }

использование вышеуказанного фильтра для передачи данных в ViewBag, который затем используется, дает тот же бесконечный вызов.

Если я не использую .Result для асинхронных вызовов, я получаю сообщение об ошибке

HttpServerUtility.Execute заблокирован во время ожидания завершения асинхронной операции.

Есть ли способ обойти эту проблему? потенциально с помощью Global.asax Application_PostAuthenticateRequest или аналогичного события?


person Tim    schedule 22.09.2015    source источник
comment
Кажется, что асинхронность не поддерживается в дочерних действиях, см. этот вопрос или этот другой вопрос. Запрос в кодеплексе был закрыт, так как добавлен в MVC 6 как часть компонентов представления.   -  person Daniel J.G.    schedule 22.09.2015
comment
Обтекание с помощью Task.Run не работает?   -  person Kędrzu    schedule 22.09.2015
comment
да, вот почему я пришел к этой проблеме после первоначальной попытки асинхронного дочернего действия.   -  person Tim    schedule 22.09.2015
comment
@Kędrzu, я не могу вспомнить, какое из состояний ошибки он выдал, но это тоже не удалось, вы можете видеть, что я закомментировал это при попытке фильтра действий.   -  person Tim    schedule 22.09.2015


Ответы (1)


Проблема здесь:

_versionInfoViewModelMapper.Map().Result;

Вызов Task.Result, а также Task.Wait() при наличии набора SynchronizationContext (что верно в среде ASP) может привести к серьезным взаимоблокировкам.

Вместо этого используйте асинхронную версию вашего метода:

public async Task<ActionResult> VersionInfo()
{
    var version = await _versionInfoViewModelMapper.Map();
    return View(version);...
}

Для получения дополнительной информации об этой проблеме см. эту запись в блоге Стивена Клири< /а>

person Kędrzu    schedule 22.09.2015