With the RIA Services July 2009 Preview release, we heard significant feedback about the DomainDataSource error handling story.  The biggest problem was when errors occurred during Load() or SubmitChanges(), server-side errors would be silently swallowed unless you opted into the LoadError, LoadedData, and SubmittedChanges events, and explicitly checked for errors.  With the PDC release, we've change this behavior, among others related to error handling.

Different Error Conditions

The DomainDataSource can encounter errors in several different ways:

  1. Load() is called when its prerequisites are not yet met.  For instance, there is no DomainContext specified, the QueryName provided is invalid, or the QueryParameters aren't all supplied.  We'll refer to this condition as "Load Prerequisites."
  2. Load() is called when another load is presently working, or when CanLoad is otherwise false.  We'll refer to this condition as "Cannot Load."
  3. Load() is called and the query expression fails to build, perhaps because of an invalid cast when getting parameter values from controls.  We'll refer to this condition as "Query Exception."
  4. Load() is called, the query is composed and invoked, but something fails on the server.  This could be authentication or authorization failures, or server-side exceptions. Let's call this a "Load Failure."
  5. SubmitChanges() is called, but some of the entities being submitted have validation errors.  This is a "Validation Failure."
  6. SubmitChanges() is called and the request is sent to the server, but something fails on the server.  Causes would be server-side validation failures, concurrency conflicts, authentication or authorization failures, or exceptions that occur on the server.  This is called a "Submit Error."

Load Prerequisites and Cannot Load

In these conditions, an exception will occur when you call Load().  The exception message will explain what prerequisite wasn't met or why Load could not be otherwise called.  When you're using AutoLoad="True" these conditions will not occur.

Query Exception and Load Failure

The Query Exception condition occurs purely client-side, without even hitting the server.  The Load Failure occurs when invoking the load method on the server.  Regardless of this difference, both conditions will result in a LoadedData event, with the LoadedDataEventArgs.HasError property set to true, and the Error property set to the exception that occurred .  Breaking Change Alert: In the July release, Query Exceptions resulted in a LoadError event; that event has been removed.  This greatly simplifies things because there are only two possibilities:

  1. Failure: LoadingData event followed by a LoadedData event with HasError = true and Error set to the exception that occurred.
  2. Success: LoadingData event followed by a LoadedData event with HasError =.false.

Again, the LoadError event has been removed with the PDC release.

Validation Failure and Submit Error

The SubmitChanges() error handling mimics what we see above with Query Exception and Load Failure.  Even though one scenario is client-side and the other is server-side, we surface them in the same manner.  Both will result in a SubmittedChanges event, with the SubmittedChangesEventArgs.HasError property set to true, and the Error property set to the exception that occurred.  Similar to load errors, there are only two possibilities with submitting changes:

  1. Failure: SubmittingChanges event followed by a SubmittedChanges event with HasError = true and Error set to the exception that occurred.
  2. Success: SubmittingChanges event followed by a SubmittedChanges event with HasError = false.

No More Silent Failures

Breaking Change Alert: DomainDataSource and RIA Services in general both aggressively report exceptions now.  If an error occurs in load or submit and you don't mark it as handled, an exception will be thrown, likely bringing down your application.  This will accomplish three things:

  1. Put the errors in your face during development so that you don't beat your head against your desk because you have no idea why things aren't working.
  2. Encourage you to handle errors in your applications, doing something explicit with them and marking them as handled.
  3. If you don't handle errors, your frustrated end users will see the exception messages with useful information to pass on to your support team.

In both LoadedData and SubmittedChanges, you should have code to mark your errors as handled.  For example:

private void MyDomainDataSource_LoadedData(object sender, LoadedDataEventArgs e)
{
    if (e.HasError)
    {
        MessageBox.Show(e.Error.ToString(), "Load Error", MessageBoxButton.OK);
        e.MarkErrorAsHandled();
    }
}
 
private void MyDomainDataSource_SubmittedChanges(object sender, SubmittedChangesEventArgs e)
{
    if (e.HasError)
    {
        MessageBox.Show(e.Error.ToString(), "Submit Error", MessageBoxButton.OK);
        e.MarkErrorAsHandled();
    }
}

When you use the Data Sources window, the LoadedData event handler is generated automatically.  But you should do more than just show the stock message box, perhaps log the error to a server and show your users a customized message.  In both LoadedData and SubmittedChanges events, calling e.MarkErrorAsHandled() will prevent the exception from being thrown from the DomainDataSource after the event is raised.

You, Less Frustrated

As I mentioned, we heard a lot of feedback about error handling with the DomainDataSource, resulting in the changes made as described herein.  I'd love to hear your feedback on these changes.  I do hope that this makes DomainDataSource usage a lot less frustrating when things go wrong.