C# 无法处理System.Progress回调异常

C# 无法处理System.Progress回调异常,c#,.net,multithreading,.net-core,C#,.net,Multithreading,.net Core,我有一个C语言的单元测试# [事实] public async void DownloadAsync\u ErrorDuringProgress\u ThrowsException() { 使用var model=SetupModel(); SetDownload(); 异步任务Act() { return wait model.downloadsync(VideoUrl,DestFile,(s,e)=> { e、 Download.ProgressUpdated+=(s,e)=> { 抛出新异

我有一个C语言的单元测试#

[事实]
public async void DownloadAsync\u ErrorDuringProgress\u ThrowsException()
{
使用var model=SetupModel();
SetDownload();
异步任务Act()
{
return wait model.downloadsync(VideoUrl,DestFile,(s,e)=>
{
e、 Download.ProgressUpdated+=(s,e)=>
{
抛出新异常(“BOOM!”);
};
});
}
//等待行动();
等待Assert.ThrowsAsync(Act);
}
如果在事件处理程序中引发异常,则整个方法应引发异常。(另一个选项是处理/忽略异常并返回DownloadStatus.Failed,但随后我必须放置一个触发分析器的通用Catch块,并且当应该在事件中处理时,用户代码中的错误被隐藏)

对于另一个类似的事件,它按预期工作,但ProgressUpdate的行为有所不同。如果我调用“wait Act();”,则会引发异常。。。但是用ThrowsAsync捕捉异常是不可能的!回调在不同的上下文或其他地方

是System.Progress类负责触发ProgressUpdated,这意味着整个回调代码段在单独的上下文中运行,该上下文不能正确处理异常

查看System.Process的文档,我发现这可能是什么原因

提供给构造函数的任何处理程序或已注册的事件处理程序 使用ProgressChanged事件通过 SynchronizationContext实例启动时捕获该实例 构建。如果当时没有当前SynchronizationContext 在构造过程中,回调将在线程池上调用

还不清楚这个问题是在运行时发生还是仅在单元测试期间发生

有人知道发生了什么,以及如何处理这样的情况吗?我觉得这里有些东西我要学

编辑:下面是发生上下文切换的代码

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; }
}
private async Task DownloadFileAsync(DownloadTaskFile fileInfo)
{
状态=下载状态。正在下载;
使用var cancelToken=new CancellationTokenSource();
尝试
{
wait_youTube.DownloadAsync(
(IStreamInfo)fileInfo.Stream、fileInfo.Destination、新进度(ProgressHandler)、cancelToken.Token.ConfigureWait(false);
无效处理程序(双百分比)
{
fileInfo.download=(长)(fileInfo.Length*百分比);
UpdateProgress();
如果(已取消)
{
尝试
{
cancelToken.Cancel();
}
捕获(ObjectDisposedException){}//以防任务已完成。
}
}
}
catch(HttpRequestException){Status=DownloadStatus.Failed;}
catch(TaskCanceledException){Status=DownloadStatus.Failed;}
}
我在哪里听说过这个未处理的错误问题?“异步无效”。此事件处理程序不是异步的,但正在异步运行。

Progress
在创建时捕获当前的
SynchronizationContext
,并在该上下文上运行其事件处理程序。
Progress
的处理程序在逻辑上是一个事件处理程序,并被视为顶级事件处理程序

这意味着无法捕获在进度处理程序之外传播的异常。它们总是直接在上下文中运行

最好的解决方法可能是调整API,使其采用
i程序
,即
Progress
并不是唯一的
IProgress
实现,事实上,听起来您可能想要一个直接执行进度处理程序的
IProgress
实现。

Progress
在创建时会捕获当前的
同步上下文,并在该上下文上运行其事件处理程序。
Progress
的处理程序在逻辑上是一个事件处理程序,并被视为顶级事件处理程序

这意味着无法捕获在进度处理程序之外传播的异常。它们总是直接在上下文中运行


最好的解决方法可能是调整API,使其采用
i程序
,即
Progress
并不是唯一的
IProgress
实现,事实上,听起来您可能需要一个直接执行进度处理程序的
IProgress
实现。

好的,这里有一堆文本,但没有具体问题。。你想要什么?让测试顺利进行。我必须能够截获异常。你确定事件处理程序被命中了吗?你能在
抛出新异常时中断并确保代码流到那里吗?并且你没有在下载函数或事件invocator中吞下任何异常吗?也许将代码发布到DownloadAsync?好的,这里有一堆文本,但没有具体问题。。你想要什么?让测试顺利进行。我必须能够截获异常。你确定事件处理程序被命中了吗?您能在
抛出新异常时中断并确保代码流到那里吗?您的下载函数或事件invocator中没有任何异常?或者将代码发布到DownloadAsync?
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; }
}