BackgroundWorker.RunWorkCompleted - Cannot Delete Exception

I am writing some kind of shell for WCF calls (using BackgroundWorker) to keep the GUI freezing during the call. It works mostly the way it should, but I have a problem with BackgroundWorker when the WCF call throws an exception. If an exception occurs in DoWork, I can detect it in RunWorkCompleted, but converting it to the GUI again does not work. I read numerous topics when people said that this should work.

Code for the shell (note that a WCF call is symbolized by a generated exception):

private void GetSomething(Action<IEnumerable<int>> completedAction) { BackgroundWorker b = new BackgroundWorker(); b.DoWork += (s, evt) => { throw new Exception(); evt.Result = new List<int> { 1, 2, 3 }; }; b.RunWorkerCompleted += (s, evt) => { if (evt.Error == null && completedAction != null) { completedAction((IEnumerable<int>)evt.Result); } else if(evt.Error != null) { throw evt.Error; } }; b.RunWorkerAsync(); } 

Calling code in a Windows form:

 private void button3_Click(object sender, EventArgs e) { try { GetSomething(list => { foreach (int i in list) { listView1.Items.Add(new ListViewItem(i.ToString())); } }); } catch (Exception ex) { MessageBox.Show(ex.Message); } } 

By debugging this, I get:

  • 'An exception of type' System.Exception 'was thrown in DoWork
  • "An exception of type" System.Exception "was thrown" at throw evt.Error
  • "TargetInvocationException was unhandled" in Application.Run (new Form1 ()) in the Main method

What am I doing wrong? I would like to get an exception in the form of Windows.

+4
source share
2 answers

The b.RunWorkerCompleted event is where you should handle error handling. You can pass an Action<Exception> to handle errors, for example

 private void GetSomething(Action<IEnumerable<int>> completedAction, Action<Exception> exceptionAction) { BackgroundWorker b = new BackgroundWorker(); b.DoWork += (s, evt) => { throw new Exception(); evt.Result = new List<int> { 1, 2, 3 }; }; b.RunWorkerCompleted += (s, evt) => { if (evt.Error == null && completedAction != null) completedAction((IEnumerable<int>)evt.Result); else if(evt.Error != null) exceptionAction(evt.Error); }; b.RunWorkerAsync(); } 

However, it tends to get ugly. If you use .Net 4 or 4.5, you can resort to tasks. Task<TResult> was created just for this case:

 Task<IEnumerable<int>> GetSomething() { return Task.Factory.StartNew(() => { Thread.Sleep(2000); throw new Exception(); return (new List<int> { 1, 2, 3 }).AsEnumerable(); }); } 

Task is basically a signal construct with

  • a .Result property
  • a .Exception property
  • a .ContinueWith() method

Inside ContinueWith() you can check if Task in a bad state (exception thrown).

You can use it as

  private void button3_Click(object sender, EventArgs e) { GetSomething() .ContinueWith(task => { if (task.IsCanceled) { } else if (task.IsFaulted) { var ex = task.Exception.InnerException; MessageBox.Show(ex.Message); } else if (task.IsCompleted) { var list = task.Result; foreach (int i in list) { listView1.Items.Add(new ListViewItem(i.ToString())); } } }); } 

If you use .Net 4.5 and C # 5 (you need VS2012 or VS2010 and Async CTP), you can even resort to async and await as

  private async void button3_Click(object sender, EventArgs e) { try { var list = await GetSomething(); foreach (int i in list) { listView1.Items.Add(new ListViewItem(i.ToString())); } } catch (Exception ex) { MessageBox.Show(ex.Message); } } 

... and all the magic is done by the compiler. Please note that you can use try catch as you are used to.

0
source

You should change this:

throw evt.Error;

For this:

MessageBox.Show(evt.Error.Message);

Your exception is not currently handled because the RunWorkerCompleted handler works later. It does not work in the try / catch that you have in button3_Click .

+1
source

Source: https://habr.com/ru/post/1443856/


All Articles