Unable to catch exception with async dataportal call (async/await)

Unable to catch exception with async dataportal call (async/await)

Old forum URL: forums.lhotka.net/forums/t/12851.aspx


Chit Swe posted on Friday, March 20, 2015

Hello,

 

I am developing window form application by using  CSLA 4.5.501.0 with visual studio 2013 and dot net 4.0. I can successfully fetch data from remote dataportal. 

 

The following is static factory method from BusinessBase class.

 

 public static async Task<PurchaseHeader> GetPurchaseVoucher(string prefix, long serial)

        {

            VoucherNoCriteria criteria = new VoucherNoCriteria()

            {

                Prefix = prefix,

                Serial = serial

            };

            PurchaseHeader purchase = await DataPortal.FetchAsync<PurchaseHeader>(criteria);

            purchase.MarkOld();

            return purchase;

     }

 

----------------------------------------------------------------------------------

 

private async void SearchPurchase(VoucherNo vno) {

            try

            {

                this.Cursor = Cursors.WaitCursor;

 

                if (vno != null)

                {

                    this.ultraStatusBar1.Text = string.Format("Loading purchase voucher ({0}-{1}).", vno.Prefix, vno.Serial);

                    this.purchase = await PurchaseHeader.GetPurchaseVoucher(vno.Prefix, vno.Serial);

                    BindData();

                    commandToolBar1.SetSavedState();

                    this.ultraStatusBar1.Text = string.Format("Done loading purchase voucher ({0}-{1}).", vno.Prefix, vno.Serial);

                }

            }

            catch (Csla.DataPortalException ex) {

 

                if (ex.BusinessException is RecordNotFoundException)

                {

                    UltraMessageBoxManager.Show("Purchase voucher cannot found.", "Purchase", MessageBoxButtons.OK, MessageBoxIcon.Information);

                }

                else if (ex.BusinessException != null)

                {

                    UltraMessageBoxManager.Show("Fail to load purchase voucher\n" + ex.BusinessException.Message, "Purchase", MessageBoxButtons.OK, MessageBoxIcon.Error);

                } 

                this.ultraStatusBar1.Text = "Ready";

            }

            catch (Exception ex)

            {

                UltraMessageBoxManager.Show("Fail to load purchase voucher\n" + ex.Message, "Purchase", MessageBoxButtons.OK, MessageBoxIcon.Error);

                this.ultraStatusBar1.Text = "Ready";

            }

            finally {

                this.Cursor = Cursors.Default;

            }

        }

 

 

 

But if awaiting  async dataportal call throw exception,  catch block is unable to catch exception and TargetInvocationException is throw at Application.Run() method in Program.cs.

 

How can I handle exception in this case.

JonnyBee replied on Sunday, March 22, 2015

Hi, 

When you use async methods that return a Task you must catch System.AggregateException that may contain several exceptions. One of these exception will be a Csla.DataPortalException.

See this article: https://msdn.microsoft.com/en-us/library/dd537614(v=vs.110).aspx

Chit Swe replied on Sunday, March 22, 2015

As you suggested, I changed my code.

private async void SearchPurchase(VoucherNo vno) {

            try

            {

                this.Cursor = Cursors.WaitCursor;

                if (vno != null)

                {

                    Program.MainForm.ShowLoading(string.Format("Loading purchase voucher ({0}-{1}).", vno.Prefix, vno.Serial));

                    this.purchase = await PurchaseHeader.GetPurchaseVoucherAsync(vno.Prefix, vno.Serial);

                    BindData();

                    commandToolBar1.SetSavedState();

                    Program.MainForm.HideLoading(string.Format("Done loading purchase voucher ({0}-{1}).", vno.Prefix, vno.Serial));

                }

 

            }

            catch (AggregateException ex)

            {

                ex.Handle(x => { HandleException("Failed to load purchase voucher", x); return true; });

            }

            catch (Csla.DataPortalException ex)

            {

                if (ex.BusinessException is RecordNotFoundException)

                {

                    Program.MainForm.HideLoading("Ready");

                    UltraMessageBoxManager.Show("Purchase voucher cannot found.", "Purchase", MessageBoxButtons.OK, MessageBoxIcon.Information);

                }

                else if (ex.BusinessException != null)

                {

                    HandleException("Fail to load purchase voucher",ex);

                } 

            }

            catch (Exception ex)

            {

                UltraMessageBoxManager.Show("Fail to load purchase voucher\n" + ex.Message, "Purchase", MessageBoxButtons.OK, MessageBoxIcon.Error);

                Program.MainForm.HideLoading("Ready");

            }

            finally {

                this.Cursor = Cursors.Default;

            }

        }

 

Here I found two interesting things.

1.If exception is thrown by server side code (in my case, I throw my custom exception 'RecordNotFoundException'), it pass to the client and I can successfully catch the exception (by DataPortalException catch block not by AggregateException catch block) that return from server as expected.

2.If exception is throw by client code (in my case, I unplugged network cable from my computer  to throw exception), the exception cannot be catch as expected and throw at Application.Run() method in Program.cs.

 

As workaround, I catch this kind of user unable to handle exception by using Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException) and 

attach event handler 

Application.ThreadException += Application_ThreadException.

But it is not perfect. Because I cannot perform required action if communication is failed.

 

Thanks

Chit Swe

Copyright (c) Marimer LLC