使用异步和匿名方法在调用程序模块中未处理异常

时间:2014-07-29 09:26:57

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

我正在测试我的类librabry,它会向网络服务器发送异步帖子。 由于要发送的数据需要不同的操作,我插入方法来处理并在blockingcollection中发送它们。永久运行的任务从集合中提取每个方法并执行它。 问题是,如果帖子失败,则错误不会冒泡到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所有相关代码。

如果我不清楚请告诉我。

1 个答案:

答案 0 :(得分:2)

你的核心问题在于:

BlockingCollection<Action>

Actionvoid - 返回委托类型,因此当您将async lambda传递给Add时,它正在创建async void方法。有几个原因可以避免async void;一个是使用try / catch

可以将代理类型更改为与async Task兼容,即BlockingCollection<Func<Task>>,假设传递给Add的所有代理均为{{1} }}。这将需要你的&#34;永远运行&#34;任务到async结果,使其代表也await。然后您需要从async更改为Task.Factory.StartNew,因为StartNew doesn't understand async delegates

但实际上,我建议使用更简单的解决方案:使用TPL Dataflow中的Task.Run(可通过NuGet获取)。