DomainService Error Handling

When errors occur server side in a DomainService during request processing, your customErrors configuration in web.config determines the level of error information that will be displayed to clients. By default the custom error mode is RemoteOnly. Deployed applications will likely use RemoteOnly or On to ensure that sensitive internal information and stack traces aren’t exposed.

 <system.web>
    <customErrors defaultRedirect="GenericError.htm" mode="RemoteOnly">
       <error statusCode="500" redirect="InternalError.htm"/>
    </customErrors>
 </system.web>

RIA Services respects this configuration when sending error information back to the client. However, while clients are getting “sanitized” error info, on the server you’ll likely want to do very detailed error logging of unhandled exceptions. For example, on the server you have full exception stack traces and context on the operation in progress (e.g. User, ChangeSet being processed, etc.), all of which can be written to the log to aid in application debugging. In previous releases there wasn’t a centralized place to do this – if you wanted to consolidate that logic, you had to override each of the three virtual DomainService Query/Submit/Invoke operations and wrap the call to base with exception handling. For example here’s a Query override:

    1: public override IEnumerable Query(QueryDescription queryDescription, 
    2:     out IEnumerable<ValidationResult> validationErrors, out int totalCount)
    3: {
    4:     IEnumerable results = null;
    5:     validationErrors = null;
    6:     totalCount = -1;
    7:  
    8:     try
    9:     {
   10:         results = base.Query(queryDescription, out validationErrors, out totalCount);
   11:     }
   12:     catch(Exception e)
   13:     {
   14:         this.LogError(e);
   15:     }
   16:  
   17:     return results;
   18: }
   19:  
   20: private void LogError(Exception e)
   21: {
   22:     // ServiceContext provides information on the operation
   23:     // being processed, current User, etc.
   24:     DomainServiceContext sc = this.ServiceContext;
   25:  
   26:     // Custom logging logic, e.g. log to a local file
   27:     // error database, etc.
   28: }

In the latest release we’ve addressed this by adding a virtual OnError method that the framework will call whenever an unhandled exception occurs. This provides a centralized place to process all unhandled exceptions:

    1: protected override void OnError(DomainServiceErrorInfo errorInfo)
    2: {
    3:     base.OnError(errorInfo);
    4:  
    5:     this.LogError(errorInfo.Error);
    6: }

The reason the parameter is the Type DomainServiceErrorInfo and not the exception itself is because we wanted to allow for the possibility of error transformation – allowing you to inspect the exception and specify a different exception to propagate back to the client. Expect to see that functionality in the next release :)

Comments

  • Anonymous
    May 29, 2010
    Thanks, this is very useful.  I was just trying to do some error transformation and then ran accross this.  Looking forward to that feature! Greg

  • Anonymous
    August 16, 2010
    Thanks for the post. I seem to get a useless stack trace for any errors that occur though. What could be the problem?   at System.Web.DomainServices.ReflectionDomainServiceDescriptionProvider.ReflectionDomainOperationEntry.Invoke(DomainService domainService, Object[] parameters)   at System.Web.DomainServices.DomainOperationEntry.Invoke(DomainService domainService, Object[] parameters, Int32& totalCount)   at System.Web.DomainServices.DomainService.Query(QueryDescription queryDescription, IEnumerable`1& validationErrors, Int32& totalCount)

  • Anonymous
    August 18, 2010
    Hmm, what is the exception message? For exceptions thrown from user methods, you should get the correct stack trace including the domain operation. I just tested this out and thats what I see.

  • Anonymous
    August 23, 2010
    The comment has been removed

  • Anonymous
    August 24, 2010
    The framework unwraps TargetInvocationExceptions so you should be getting the right stack trace. I've not heard anyone complain of this issue before. If you email me a repro I could take a look.

  • Anonymous
    August 25, 2010
    Hi I've put together an example project at dl.dropbox.com/.../DomainServiceErrorHandling.zip Thanks

  • Anonymous
    August 25, 2010
    I see the issue - I assumed initially that you were on the .NET 4.0 RTM bits. You're using the pre RTM .NET 3.5 PDC drop. You're likely running into a bug that has subsequently been fixed in the RTM bits. So unless you move to the RTM version, I'm afraid you won't be able to get around this.

  • Anonymous
    August 25, 2010
    Thanks Matthew. Unfortunately we haven't yet upgraded to VS 2010, so will have to use the PDC beta. As a workaround I'm going to add try... catch to every method which will call a private method to log the exception.

  • Anonymous
    August 26, 2010
    As a workaround I've done a similar solution to your pre-OnError method code, but also added logic to pass the exception back to the Silverlight client when debugging or when certain types of exception occur. forums.silverlight.net/.../462107.aspx

  • Anonymous
    November 08, 2010
    Hi. How do i meke the error transformation in the protected override void OnError(DomainServiceErrorInfo errorInfo)   2: { 3:     base.OnError(errorInfo);   4:     5:     this.LogError(errorInfo.Error);   6: } thanks in advance

  • Anonymous
    November 08, 2010
    Juan - to "transform" the error, you can inspect the exception information in errorInfo.Error and set a completely new exception (errorInfo.Error = myError).

  • Anonymous
    November 08, 2010
    Thanks for the fast reply. Could you make a brief example? ie. i´m trying to catch a sql exception and show a descriptive message to the client. i needs to know how to catch by type.(in this case a sqlException) and in the client, how to print, in the on error in the domainservice  i have: protected override void OnError(DomainServiceErrorInfo errorInfo)        {            BLLException be = new BLLException("Here should put a message", errorInfo.Error.InnerException); } in the client:  private void observacionDomainDataSource_LoadedData(object sender, LoadedDataEventArgs e)        {            if (e.HasError)            {               System.Windows.MessageBox.Show(e.Error.Message.ToString(), "Error en la carga", System.Windows.MessageBoxButton.OK);                e.MarkErrorAsHandled();            }        } thanks in advance

  • Anonymous
    May 26, 2011
    Great post… now I understand type moment. Works in SL4 WCF RIA as advertised.