У меня есть этот модульный тест в C#
[Fact]
public async void DownloadAsync_ErrorDuringProgress_ThrowsException()
{
using var model = SetupModel();
SetDownload();
async Task<DownloadStatus> Act()
{
return await model.DownloadAsync(VideoUrl, DestFile, (s, e) =>
{
e.Download.ProgressUpdated += (s, e) =>
{
throw new Exception("BOOM!");
};
});
}
// await Act();
await Assert.ThrowsAsync<Exception>(Act);
}
Если в обработчике события возникает исключение, тогда весь метод должен генерировать исключение. (Другой вариант - обработать / игнорировать исключение и вернуть DownloadStatus.Failed, но затем я должен поместить универсальный блок c Catch, который запускает анализаторы, и ошибка в коде пользователя скрыта, когда она должна обрабатываться в событии. ).
Для другого подобного события оно работает как положено, но ProgressUpdated ведет себя по-другому. Если я вызываю «await Act ();», тогда выдается исключение ... но с ThrowsAsyn c невозможно отловить это исключение! Обратный вызов каким-то образом находится в другом контексте или что-то в этом роде.
Это класс System.Progress, который отвечает за запуск ProgressUpdated - это означает, что весь фрагмент кода обратного вызова выполняется в отдельном контексте, который не правильно обрабатывать исключения.
Глядя в документацию System.Process, я обнаружил, что это может быть причиной?
Любой обработчик, предоставленный конструктору или обработчикам событий, зарегистрированным в ProgressChanged событие вызывается через экземпляр SynchronizationContext, захваченный при создании экземпляра. Если во время создания нет текущего SynchronizationContext, обратные вызовы будут вызываться в ThreadPool.
Также не ясно, будет ли эта проблема возникать во время выполнения или только во время модульного тестирования.
Кто-нибудь понимает, что происходит, и как справиться с такой ситуацией? Я чувствую, что мне есть чему поучиться здесь.
Редактировать: Вот код, где произойдет переключение контекста
private async Task DownloadFileAsync(DownloadTaskFile fileInfo)
{
Status = DownloadStatus.Downloading;
using var cancelToken = new CancellationTokenSource();
try
{
await _youTube.DownloadAsync(
(IStreamInfo)fileInfo.Stream, fileInfo.Destination, new Progress<double>(ProgressHandler), cancelToken.Token).ConfigureAwait(false);
void ProgressHandler(double percent)
{
fileInfo.Downloaded = (long)(fileInfo.Length * percent);
UpdateProgress();
if (IsCancelled)
{
try
{
cancelToken.Cancel();
}
catch (ObjectDisposedException) { } // In case task is already done.
}
}
}
catch (HttpRequestException) { Status = DownloadStatus.Failed; }
catch (TaskCanceledException) { Status = DownloadStatus.Failed; }
}
Где еще я услышал об этой проблеме необработанных ошибок? "asyn c void". Этот обработчик событий не asyn c, но он запускается asyn c.