在Task.Factory.StartNew(()=>中捕获TargetInvocationException

时间:2015-06-19 06:40:18

标签: c# task-parallel-library task

我正在使用C#,.NET Framework 4.0和Visual Studio 2012 Premium开发Windows窗体应用程序。

我有这个方法:

private void firstPhaseBtn_Click(object sender, EventArgs e)
{
    var task = Task.Factory.StartNew(() =>
    {
        if (_viewModel == null)
            _viewModel = new MainViewModel();

        this.BeginInvoke((Action)delegate()
        {
            labelLoading.Text = "Creando orden...";
            labelLoading.Visible = true;

            Models.FirstPhaseModel model = new Models.FirstPhaseModel()
            {
                // Set data.
            };

            orderNumberLabel.Text = _viewModel.FirstPhase(model);

            firstPhaseBtn.Enabled = true;
            labelLoading.Text = string.Empty;
            labelLoading.Visible = false;
        });
    });

    try
    {
        task.Wait();
    }
    catch (Exception)
    {
        MessageBox.Show(this, _viewModel.ClientCustomError, "Error");
    }
}

_viewModel.FirstPhase(model);上,我对网络服务进行HTTP获取请求。

我的问题是try catch块不起作用。我总是得到一个未经处理的例外。

我试图在Debug,Release中运行项目,并在Release文件夹上运行可执行文件,但我总是得到一个未处理的异常。

我还尝试将try catch块放在任务中,但结果相同。

如何在任务中处理异常?

我也试过添加这个:

.ContinueWith(t =>
            {
                labelLoading.Visible = false;
                labelLoading.Text = string.Empty;
                MessageBox.Show(this, _viewModel.ClientCustomError, "Error");
            },  TaskContinuationOptions.OnlyOnFaulted);

但我仍然得到System.Reflection.TargetInvocationException

2 个答案:

答案 0 :(得分:1)

问题出在这里:

this.BeginInvoke((Action)delegate()
    {
        labelLoading.Text = "Creando orden...";
        labelLoading.Visible = true;

        Models.FirstPhaseModel model = new Models.FirstPhaseModel()
        {
            // Set data.
        };

        orderNumberLabel.Text = _viewModel.FirstPhase(model);

        firstPhaseBtn.Enabled = true;
        labelLoading.Text = string.Empty;
        labelLoading.Visible = false;
    });

一旦你调用BeginInvoke,你就可以在UI线程上有效地运行任务本身,这反过来意味着TPL不再能够跟踪和跟踪异常并将它们组合到一起AggregateException。

这是一种更容易证明问题​​的方法:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    protected override void OnShown(EventArgs e)
    {
        base.OnShown(e);
        var task = Task.Factory.StartNew(() =>
        {
            Thread.Sleep(1000);
            BeginInvoke((Action)delegate { throw new NotImplementedException(); });
        });

        try
        {
            task.Wait();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

如果您运行此代码,它将使应用程序崩溃(除非您已订阅相关的catch所有异常处理程序,我不记得了)。

您需要做的是移出BeginInvoke代码(在正确的调度程序上使用任务继续)或尝试/捕获BeginInvoke()内的全部代码。

<强>更新

这是我重写代码的方式:

var task = Task.Factory.StartNew(() =>
        {
            if (_viewModel == null)
            {
                _viewModel = new MainViewModel();
            }
        }).
            ContinueWith(x =>
            {

                labelLoading.Text = "Creando orden...";
                labelLoading.Visible = true;

                Models.FirstPhaseModel model = new Models.FirstPhaseModel()
                {
                    // Set data.
                };

                orderNumberLabel.Text = _viewModel.FirstPhase(model);

                firstPhaseBtn.Enabled = true;
                labelLoading.Text = string.Empty;
                labelLoading.Visible = false;
            }, TaskScheduler.Current).ContinueWith(result =>
            {
                if (result.IsFaulted)
                {
                    // do something with the result and "consume" it.
                    _log.Error(result.Exception);
                }
            });

答案 1 :(得分:0)

这就是我解决问题的方法:

private void firstPhaseBtn_Click(object sender, EventArgs e)
{
    string orderNumber = string.Empty;

    labelLoading.Text = "Creando orden...";
    labelLoading.Visible = true;

    Models.FirstPhaseModel model = new Models.FirstPhaseModel()
    {
        // Data...
    };

    var task = Task.Factory.StartNew(() =>
    {
        if (_viewModel == null)
            _viewModel = new MainViewModel();

        orderNumber = _viewModel.FirstPhase(model);
    })
    .ContinueWith(result =>
    {
        if (result.IsFaulted)
        {
            this.BeginInvoke((Action)delegate()
            {
                labelLoading.Visible = false;
                labelLoading.Text = string.Empty;
                MessageBox.Show(this, _viewModel.ClientCustomError, "Error");
            });
        }
        else
        {
            this.BeginInvoke((Action)delegate()
            {
                orderNumberLabel.Text = orderNumber;

                firstPhaseBtn.Enabled = false;
                labelLoading.Visible = false;
                labelLoading.Text = string.Empty;
            });

            ShowStaticAttributes();
        }
    });
}

感谢您的回答和帮助。