【问题标题】:Exception not handled in caller module with async and anonymous methods [closed]使用异步和匿名方法在调用者模块中未处理异常[关闭]
【发布时间】:2014-07-29 21:25:16
【问题描述】:

我正在测试我的类库,它将异步发布到网络服务器。 由于要发送的数据需要不同的操作,我插入方法来处理并将它们发送到阻塞集合中。一个永远运行的任务从集合中提取每个方法并执行它。 问题是如果发布失败,错误不会冒泡到 wpf 调用者模块。

这是库模块

private Task queueInvio;
private BlockingCollection<Action> codaInvio = null;

public MotoreClient()
    {
        codaInvio = new BlockingCollection<Action>();

        queueInvio = Task.Factory.StartNew(() =>
        {
            while (true)
            {
                Action azione = null;
                if (codaInvio.TryTake(out azione))
                {
                    try
                    {
                        azione();
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(ex.Message);
                    }
                }
            }
        }
        , CancellationToken.None
        , TaskCreationOptions.LongRunning
        , TaskScheduler.Default);
    }

这是wpf测试程序调用的方法

        public void InviaAggiornamento(TipoAggiornamento tipoAggiornamento)
    {
        string nomePaginaApi = String.Empty;
        HttpContent contenuto = null;

        switch (tipoAggiornamento)
        {
            // blah blah code
        }

        // exception capture here, but not rethrown to the wpf module
        codaInvio.Add(async () =>
        {
            try
            {
                await InviaAggiornamento(nomePaginaApi, contenuto);
            }
            catch (Exception ex)
            {
                throw;
            }

        });
    }

这是制作异步帖子的方法

private async Task InviaAggiornamento(string nomePaginaApi, HttpContent contenuto)
    {
        HttpClient httpClient = new HttpClient();
        string riposta = String.Empty;

        if (!string.IsNullOrEmpty(indirizzoServer))
        {
            try
            {
                httpClient.BaseAddress = new Uri(IndirizzoServer);
                httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", USERNAME, PASSWORD))));

                var response = await httpClient.PostAsync("api/liveTimingApi/" + nomePaginaApi, contenuto);

                if (response.StatusCode != HttpStatusCode.NoContent)
                    throw new Exception("Richiesta PostAsync fallita.");

                if (!response.IsSuccessStatusCode)
                {
                    string rispostaErrore = string.Empty;

                    if (!string.IsNullOrEmpty(response.ReasonPhrase))
                        rispostaErrore += " ReasonPhrase: " + response.ReasonPhrase;

                    if (!string.IsNullOrEmpty(response.Content.ReadAsStringAsync().Result))
                        rispostaErrore += " Result: " + response.Content.ReadAsStringAsync().Result;

                    throw new ServerException(rispostaErrore.Trim());
                }
            }
            catch (HttpRequestException hre)
            {
                throw new Exception("HttpRequestException: " + hre.Message);
            }
            catch (TaskCanceledException)
            {
                throw new Exception("Richiesta cancellata (TaskCanceledException).");
            }
            catch (Exception ex)
            {
                throw new Exception("Exception: " + ex.Message);
            }
            finally
            {
                if (httpClient != null)
                {
                    httpClient.Dispose();
                    httpClient = null;
                }
            }
        }
    }

这是模拟数据发送的 wpf 模块

        private void btnSendTest_Click(object sender, RoutedEventArgs e)
    {
        motoreClient.IndirizzoServer = "http://localhost:721";
        motoreClient.AggiungiRigaProgrammaOrario(1, 1, "GaraDescrizione", DateTime.Now, "XXX", "SessioneDescrizione", "1:00:00", true);

        try
        {
            motoreClient.InviaAggiornamento(TipoAggiornamento.ProgrammaOrario);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

由于 IndirizzoServer(服务器地址)是假的,我有一个 HttpRequest 异常。 我在 codaInvio.Add try/catch 块中捕获它,但我无法将它重新抛出到调用方 wpf 模块。 Visual Studio 说异常不是由调用者代码处理的。 为什么?我正在使用 try/catch 所有相关的代码。

如果我不清楚,请告诉我。

马可

【问题讨论】:

  • 如果您可以将这些 sn-ps 重构为一个简短但完整的程序,我们可以轻松复制、粘贴、编译和运行,这将非常有帮助...
  • 给你。带有解决方案的拉链……只需按下按钮。我正在等待一个消息框出现,而是在 codaInvio.Add() uploadmb.com/dw.php?id=1406633237
  • 不,我没有要求 zip 文件。这实际上是 WPF 特定的吗?您是否在控制台应用程序中尝试过?您可能可以在一个足够短的程序中重现它,只需直接粘贴...

标签: c# asynchronous task-parallel-library task async-await


【解决方案1】:

你的核心问题在这里:

BlockingCollection<Action>

Action 是一个void-returning 委托类型,因此当您将async lambda 传递给Add 时,它正在创建一个async void 方法。避免async void 有几个原因;一是不可能使用try/catch捕获异常。

可以将委托类型更改为与async Task 兼容,即BlockingCollection&lt;Func&lt;Task&gt;&gt;,假设所有传递给Add 的委托都是async。这将要求您的“永远运行”任务以await 为结果,使其代表也为async。然后你需要从Task.Factory.StartNew 更改为Task.Run,因为StartNew doesn't understand async delegates

但实际上,我会推荐一个更简单的解决方案:使用来自 TPL Dataflow 的 ActionBlock(可通过 NuGet 获得)。

【讨论】:

    猜你喜欢
    • 2015-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-02
    • 2020-12-26
    • 2017-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多