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 (13)
  1. Greg Hollywood says:

    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

  2. Rob Brown says:

    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)

  3. 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.

  4. Rob Brown says:

    Hi,

    The exception message is the correct message for the actual exception. I made an Invoke method like so:-

    public int ErrorTest()

           {

                   int myNum = 0;

                   return 2323 / myNum;

           }

    Calling this gives errorInfo.Error.Message as "Attempted to divide by zero.".

    But I've no idea where it occurred as errorInfo.Error.StackTrace gives

    "   at System.Web.DomainServices.ReflectionDomainServiceDescriptionProvider.ReflectionDomainOperationEntry.Invoke(DomainService domainService, Object[] parameters)rn   at System.Web.DomainServices.DomainService.Invoke(DomainOperationEntry operation, Object[] parameters, IEnumerable`1& validationErrors)"

    Any ideas why I'm not getting a full stack trace back including line numbers? The site is compiled in Debug mode with Debug Info set to full.

    Thanks in advance. I hope you can help me, otherwise my app will be a nightmare to debug if errors happen in production.

  5. 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.

  6. RobIntechnica says:

    Hi

    I've put together an example project at dl.dropbox.com/…/DomainServiceErrorHandling.zip

    Thanks

  7. 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.

  8. RobIntechnica says:

    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.

  9. RobIntechnica says:

    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

  10. Juan says:

    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

  11. Juan – to "transform" the error, you can inspect the exception information in errorInfo.Error and set a completely new exception (errorInfo.Error = myError).

  12. Juan says:

    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

  13. James Hall says:

    Great post… now I understand type moment. Works in SL4 WCF RIA as advertised.

Comments are closed.

Skip to main content