2017-02-22 14 views
1

Я пытался объяснить коллеге, почему функции async void плохи и что исключения не будут пойманы, но, оказывается, я, возможно, не понимаю их правильно. У нас есть кусок кода, который выглядит немного как это:Функция MVC возвращает, но ждет асинхронной функции перед отправкой результата

public ActionResult EmailCandidates(List<string> identityTokens, string subject, string content) 
{ 
    // generate list of recipients here 
    SendEmail(recipients, subject, content); //not awaited 
    return new AjaxResponse { // AjaxResponse is a wrapper around JSONResponse 
     IsSuccess = true, 
     Data = recipients.Select(r=>r.Name) 
    }; 
} 

private async void SendEmail(List<EmailAddress> recipients, string subject, string content) 
{ 
    await Task.Delay(10000); // simulate async send email 
    throw new Exception(); // manually added 
} 

То, что я ожидал, и то, что я пытался объяснить, что если функция SendEmail бросает исключение не будет пойман должным образом, потому что основная функция EmailCandidates уже вернулась к клиенту. Только это не так. Код выше выполняется в точном порядке, я ожидаю:

  • вызова сделан от клиента EmailCandidates
  • SendEmail называется
  • письмо отправляется асинхронно (моделируется здесь через асинхронное ожидание)
  • управление возвращается к EmailCandidates, и возвращение выполняется

, а затем он получает довольно странно:

  • На данный момент, я ожидал получить ответ клиенту, но я не делаю, даже если EmailCandidates вернулся
  • 10 секунд спустя исключение
  • исключение перехватывается глобальной ошибки обработчик, и теперь клиент получает ошибку 500 (неудивительно)

Так почему же хотя EmailCandidates вернулся, ответ не отправляется клиенту. Как знать, ждать функции async SendEmail?

+0

быстрый вопрос, что у вас есть на этой конфигурации, которая находится внутри вашего webconfig? Zinov

+0

@ Zinov веб-конфигурация довольно велика, но пользовательская ошибка '' – Anduril

+0

проверить ниже мой ответ – Zinov

ответ

3

ASP.NET предоставляет SynchronizationContext, который отслеживает количество асинхронных операций в полете и не будет отправлять результат до тех пор, пока все они не будут завершены. Обратите внимание, что этот SynchronizationContext был удален в ASP.NET Core.

Однако вы не должны видеть это поведение даже на ASP.NET. В случае синхронного метода, вызывающего метод async void, вы должны увидеть InvalidOperationException с сообщением «Асинхронная операция не может быть запущена в это время». В случае асинхронного метода, вызывающего метод async void (который не завершается до возвращения обработчика), вы должны увидеть InvalidOperationException с сообщением «Асинхронный модуль или обработчик завершен, пока асинхронная операция еще не выполнена».

Поскольку ни одна из этих сетей безопасности не запускается, я подозреваю, что ваш код ASP.NET использует старую версию ASP.NET. Эти системы безопасности были добавлены в .NET 4.5, которые вы должны не только иметь в качестве цели сборки, но и у вас есть также должны add targetFramework in your web.config.

Новый .NET 4.5.2 ASP.NET MVC приложение с помощью следующего кода сразу бросает InvalidOperationException, как и ожидался:

public ActionResult About() 
{ 
    ViewBag.Message = "Your application description page."; 
    Test(); 
    return View(); 
} 

private async void Test() 
{ 
    await Task.Delay(20000); 
    throw new Exception("Blah"); 
} 
+0

Мы _are_, используя старую версию .NET. Фактически, вся эта проблема возникла из-за того, что мы установили флаг 'UseTaskFriendlySynchronizationContext' в веб-config для работы с signalr, который мы только что установили. Затем мы получили сообщение об ошибке. Я не знал, что MVC ждал всех асинхронных функций, прежде чем вернуться. Очень интересно – Anduril

+0

@Anduril: 'UseTaskFriendlySynchronizationContext' * должен * быть установлен в' true', иначе 'async' /' await' может вообще не работать должным образом. –

+0

В настоящий момент мы не используем какой-либо код асинхронного/ожидающего (кроме этой функции, о которой я не знал, даже был похоронен в малоиспользуемой части сайта), поэтому нам удалось избежать этого далеко. Предположительно, нам не нужно устанавливать значение 'UseTaskFriendlySynchronizationContext' в true, если мы установим' targetFramework' в 4.5 или новее? – Anduril

0

Async недействительных методы своего родом разные звери из «нормальных» асинхронных методов. У них разная логика обработки ошибок.Когда исключение выбрасывается из задачи async Task или async Task, это исключение захватывается и помещается в объект Task. С помощью методов async void нет объекта Task, поэтому любые исключения, создаваемые методом async void, будут возникать непосредственно в SynchronizationContext, который был активен, когда был вызван метод async void. Эти исключения можно наблюдать с помощью обработчика событий UnhandledException.

0

Ваше приложение работает нормально, фактически выполняет поведение по умолчанию, которое имеет MVC. Если вы явно укажете исключение, то такие ошибки (500) возникают, когда запрос создается с того же компьютера, на котором приложение включено (localhost), если вы хотите увидеть, что фактический пользователь хочет увидеть, вам нужно изменить значение для webconfig для параметра По умолчанию для него установлено значение RemoteOnly.

Если вы перейдете на свой фильтр FilterConfig. Вы увидите эту строку кода

public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
     { 
      filters.Add(new HandleErrorAttribute()); 
     } 

Попробуйте изменить значение «On», и вы увидите, что вы оказались на этой странице ошибку, поскольку атрибут ошибки обработчика, он обеспечивает логику обработки пост на действия и когда он видит, что исключение ускользнуло от действия, оно отображает представление ошибки вместо желтого экрана смерти. Вид ошибки в приложении по умолчанию внутри View/Shared/Error.cshtml

Ваш Офф вариант

Указывает, что пользовательские ошибки отключены. Это позволяет отображать подробные ошибки .

для справки идут сюда: https://msdn.microsoft.com/en-us/library/h0hfz6fc(v=vs.71).aspx

Если вы положили пульт только тогда вы будете продолжать видеть ошибки при отладке веб-сайта в LocalMachine, но если вы размещаете приложение конечный пользователь не увидит эта ошибка.

Смежные вопросы