Что такое AggregateException в C#?
Что проверяют
Знаете ли вы, как исключения ведут себя в задачах, и понимаете ли разницу между ожиданием через await и блокирующим ожиданием. Вопрос звучит словарно, но почти всегда переходит в практику: почему в одном месте ловится AggregateException, а в другом обычное исключение.
Как отвечать
AggregateException это исключение-контейнер, внутри которого коллекция InnerExceptions. Он нужен там, где упасть могла не одна операция, а сразу несколько, и терять часть ошибок нельзя.
Где он появляется: Task.Wait, обращение к Result, Task.WaitAll, Parallel.For и ForEach, PLINQ. Здесь ошибки всех участников заворачиваются в один объект.
Ключевая деталь, ради которой вопрос и задают: await разворачивает контейнер и выбрасывает только первое внутреннее исключение, чтобы обычный catch по типу продолжал работать. Поэтому await Task.WhenAll покажет одну ошибку, даже если упали три.
var all = Task.WhenAll(tasks);
try { await all; }
catch { foreach (var e in all.Exception.InnerExceptions) Log(e); }Полезно назвать Flatten, который разворачивает вложенные контейнеры от вложенных задач, и Handle, который гасит ожидаемые исключения и пробрасывает остальные дальше.
Где спотыкаются
Ждут AggregateException после await и пишут catch именно на него. Блок не срабатывает никогда, ошибка уходит наверх, а в логе остаётся одна строка вместо трёх.
Берут только InnerException в единственном числе. Первое исключение попадает в лог, остальные исчезают, и настоящая причина падения регулярно оказывается во втором.
Смешивают блокирующее ожидание с асинхронным кодом. Result и Wait в контексте с синхронизацией дают взаимоблокировку, и вместо понятной ошибки получается зависший запрос, который потом объясняют чем угодно, только не этим.