CSLA Light: Question regarding exception handling in DataPortalClient.WcfProxy

CSLA Light: Question regarding exception handling in DataPortalClient.WcfProxy

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


paupdb posted on Thursday, September 11, 2008

This is a question for the Magenic guys primarily.

I ran into an interesting issue today while debugging a problem in one of my callback methods.

Basically my callback method was halfway through exception following a successful dataportal fetch and was then throwing up an unhandled exception.
Now I expected this exception to bubble all the way up to a "critical error" handler I had implemented on the Silverlight App class (i.e. the UnhandledException event on the App was attached to Application_UnhandledException in my App.xaml.cs).

Problem was that instead of the exception percolating up, I found that my callback method was being called again.
This was kind of weird but it became clear why this was happening when I stepped through line by line into the WcfProxy to the code below:

    private void proxy_FetchCompleted(object sender, Csla.WcfPortal.FetchCompletedEventArgs e)
    {
      try
      {
        if (e.Error == null && e.Result.ErrorData == null)
        {
          var buffer = new System.IO.MemoryStream(e.Result.ObjectData);
          var formatter = new MobileFormatter();
          T obj = (T)formatter.Deserialize(buffer);
          _globalContext = (ContextDictionary)MobileFormatter.Deserialize(e.Result.GlobalContext);
          OnFetchCompleted(new DataPortalResult<T>(obj, null, e.UserState));
        }
        else if (e.Error != null)
        {
          var ex = new DataPortalException(e.Error.ToErrorInfo());
          OnFetchCompleted(new DataPortalResult<T>(default(T), ex, e.UserState));
        }
        else if (e.Result.ErrorData != null)
        {
          var ex = new DataPortalException(e.Result.ErrorData);
          OnFetchCompleted(new DataPortalResult<T>(default(T), ex, e.UserState));
        }
        else
          throw new InvalidOperationException("Server must return an object or an error");
      }
      catch (Exception ex)
      {
        OnFetchCompleted(new DataPortalResult<T>(default(T), ex, e.UserState));
      }
    }


My issue with the above is that the exception handler will be hit, even if the exception comes from within the OnFetchCompleted method (i.e. from within my callback).  So the exceptions never percolate past the WcfProxy up to the Application_UnhandledException in the Silverlight App.
Then to compound my issue, the exception handler then proceeds to invoke the callback again, this time with a blank result, which really isn't great if the exception occurred inside the callback in the first place.

Now I know what I could do is implement try catch blocks within all my callbacks, and I'm OK with that if this is the desired pattern you guys want CSLA to enforce. 
But obviously this is extra work I didn't necessarily want to be doing, plus having catchall try blocks in each callback doesn't look very nice to me.

I'd like to suggest modifying the above WcfProxy code, moving the exception handler to only trap issues encountered with the code prior to invoking the callback method.
So something like this:

private void proxy_FetchCompleted(object sender, Csla.WcfPortal.FetchCompletedEventArgs e)
    {
      if (e.Error == null && e.Result.ErrorData == null)
      {
        try {
          var buffer = new System.IO.MemoryStream(e.Result.ObjectData);
          var formatter = new MobileFormatter();
          T obj = (T)formatter.Deserialize(buffer);
          _globalContext = (ContextDictionary)MobileFormatter.Deserialize(e.Result.GlobalContext);
        }
        catch (Exception ex)
        {
          OnFetchCompleted(new DataPortalResult<T>(default(T), ex, e.UserState));
        }

        OnFetchCompleted(new DataPortalResult<T>(obj, null, e.UserState));
      }
      else if (e.Error != null)
      {
        var ex = new DataPortalException(e.Error.ToErrorInfo());
        OnFetchCompleted(new DataPortalResult<T>(default(T), ex, e.UserState));
      }
      else if (e.Result.ErrorData != null)
      {
        var ex = new DataPortalException(e.Result.ErrorData);
        OnFetchCompleted(new DataPortalResult<T>(default(T), ex, e.UserState));
      }
      else
        throw new InvalidOperationException("Server must return an object or an error");
    }
  }

This way the exceptions continue up the stack, and there isnt a risk of a callback being invoked twice.

Thanks

Paul

Copyright (c) Marimer LLC