In Silverlight2, dopo avere creato il nostro bel servizio WCF che genera una serie di eccezioni (ovviamente di tipo FaultException/FaultException<T>) quando c’è qualcosa che non va, è triste scoprire che di queste eccezioni non ce ne facciamo nulla in quanto non verranno mai intercettate dalla nostra applicazione Silverlight per una lunga serie di motivi.

Fortunatamente questa è acqua passata in quanto nella versione 3 le eccezioni vengono correttamente gestite dal plug-in a patto che venga iniettato del codice per aggirare il problema principale: Le eccezioni vengono ritornate con un HTTP Response Code=500 e quindi vengono intercettate dal browser non raggiungendo mai il plug-in Silverlight.

Il trick consiste nel utilizzare un Message Inspector che trasformi un HttpResponseCode da 500 a 200 in modo che la risposta possa arrivare al plug-in Silverlight.
Il codice è disponibile qui: http://code.msdn.microsoft.com/silverlightws/Release/ProjectReleases.aspx?ReleaseId=1660  mentre un estratto di codice per il nostro caso è quello che segue (giusto per capire quali sono i namespaces che ho definito)

   1: namespace CustomBehaviors
   2: {
   3:     public class SilverlightFaultBehavior : BehaviorExtensionElement, IEndpointBehavior
   4:     {
   5:         ...
   6:     }
   7: }

A questo punto dobbiamo iniettare il custom behavior via Web.config:

   1: <system.serviceModel>
   2:     <extensions>
   3:         <behaviorExtensions>
   4:             <!--Defines custom message inspector-->
   5:             <add name="silverlightFaults"
   6:                   type="CustomBehaviors.SilverlightFaultBehavior, CustomBehaviors, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>                
   7:         </behaviorExtensions>
   8:     </extensions>
   9:  
  10:     <behaviors>
  11:         <endpointBehaviors>
  12:             <!--Custom behavior for message inspection-->
  13:             <behavior name="silverlightFaultBehavior">
  14:                 <silverlightFaults/>
  15:             </behavior>
  16:         </endpointBehaviors>
  17:         <serviceBehaviors>
  18:             <behavior name="YouBook.Web.YouBookDataServiceBehavior">
  19:                 <serviceMetadata httpGetEnabled="true"/>
  20:                 <serviceDebug includeExceptionDetailInFaults="false"/>
  21:             </behavior>
  22:         </serviceBehaviors>
  23:     </behaviors>
  24:     <bindings>
  25:         <customBinding>
  26:             <binding name="CustomBinding">
  27:                 <binaryMessageEncoding>
  28:                     <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
  29:                 </binaryMessageEncoding>
  30:                 <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647"/>
  31:             </binding>
  32:         </customBinding>
  33:     </bindings>
  34:     <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
  35:     <services>
  36:         <service behaviorConfiguration="YouBook.Web.YouBookDataServiceBehavior" name="YouBook.Web.YouBookDataService">
  37:             <endpoint address="" 
  38:                          binding="customBinding" 
  39:                          bindingConfiguration="CustomBinding" 
  40:                          contract="YouBook.Web.IYouBookDataService"
  41:                          behaviorConfiguration="silverlightFaultBehavior"
  42:                          />
  43:             <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
  44:         </service>
  45:     </services>
  46: </system.serviceModel>

That’s all!

Supponendo di generare una FaultException lato server in questo modo:

   1: try
   2: {
   3:     YouBookDataContext db = new YouBookDataContext();
   4:     User loggedUser = db.Users.FirstOrDefault(user => user.UserName == userName && user.Password == password);                    
   5:     return loggedUser;
   6: }
   7: catch (Exception ex)
   8: {
   9:     FaultReason reason = new FaultReason(ex.Message);
  10:     throw new FaultException(reason);
  11: }

Lato client possiamo, finalmente, gestire questo caso usando un banalissimo blocco try/catch.

   1: private void proxy_LoginCompleted(object sender, LoginCompletedEventArgs e)
   2: {
   3:     if (e.Error != null)
   4:     {
   5:         FaultException<LoginFault> loginException = e.Error as FaultException<LoginFault>;
   6:  
   7:         if (loginException != null)
   8:         {
   9:             UserDialog dialog = new UserDialog("Login Failed", loginException.Detail.Reason);
  10:             dialog.Show();
  11:         }
  12:         return;
  13:     }
  14:     ...
  15:  
  16: }

Speriamo che nella RTM semplifichino la gestione del cambio di HTTP Response Code cosi che intercettare le eccezioni lato server sia ancora più facile.