Free Trial

Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.

  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • PrintPrint
Share this Page URL
Help

8. Talking to WCF and ASMX Services > Debugging a service in Silverlight

Debugging a service in Silverlight

Debugging a service in Silverlight Applies to Silverlight 3, 4 and 5 One of the things that were missing in Silverlight 2 when it came to working with services was the ability to debug them. In the event of an error occurring in the service code, it was not possible to get information about this error in the Silverlight client application. The only information we got when something went wrong was The remote server returned an error: NotFound, and that was it. The user wasn't provided with any inner exception with a clue about the real error on the service, any messages, and so on. While this was a problem during development, it was a worse a problem that we could not give any indication to the end user on what he/she should do to solve the issue. This was because we could not distinguish let's say a divide-by-zero error from an error caused by the database being down inside the Silverlight application. If we are developing both the service and the Silverlight application, we can still debug the service code and find out the eventual errors. However, it's a real show-stopper when working with external, perhaps third-party services to which we don't have access as we can't see what's happening on the service side. Luckily, Silverlight has evolved quite a lot since version 2, as it now includes the option to debug the service by displaying the returned error from the service side. In this recipe, we'll look at how to set this up. Getting ready To try out this sample, a starter solution is provided in the Chapter08/SilverlightServiceDebugging_Starter folder in the downloads available on the Packt website. The finished solution can be found in the Chapter08/SilverlightServiceDebugging_Completed folder. How to do it... To explain the way Silverlight makes it possible to debug a service, we'll start from an application that could have been easily created in Silverlight 2, and we'll follow the changes we need to make to both the service and the Silverlight application. We do this so that we can debug the service and get error information in the Silverlight client. The scenario is very simple. The application allows us to search for an employee or a list of employees in a directory based on username. If a result is found, a DataGrid is shown containing the results. Let's take a look at the steps we need to perform to make it possible to debug this application: Note Note that this recipe requires either Fiddler or Web Development Helper to view the traffic from and to the service. See the Appendix for more info on these. We'll use Fiddler in this recipe. Open the solution located in the Chapter08/SilverlightServiceDebugging_Starter folder in the code downloads and take a look at the SilverlightEmployeeLookup.Web/EmployeeLookupService.svc.cs file. The RetrieveEmployeesByUserName(string userName) method has a bug residing in this class. If no Employee items are found, we still try to retrieve the first item. Of course, this will generate an error. The following code snippet shows the faulty code: if (results.Count<Employee>() == 0) { Employee employee = results.First(); } Let's try if we can see in the Silverlight application what went wrong. Run the application and search for George. Visual Studio will show the sequence contains no element's error inside the service code. However, on the client side, the error information is not available anymore, and the dreaded NotFound exception is thrown, as shown in the following screenshot: At this point, even Fiddler is of no help. Let's run the application with Fiddler open again. When the error occurs, a message with status 500 is shown, as well as the following result is shown, containing no information about the error: <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <s:Fault> <faultcode xmlns:a="http://schemas.microsoft.com/ net/2005/12/windowscommunicationfoundation/dispatcher"> a:InternalServiceFault </faultcode> <faultstring xml:lang="nl-BE"> The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the &lt;serviceDebug&gt; configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs. </faultstring> </s:Fault> </s:Body> </s:Envelope> The previous message says that we need to turn on IncludeExceptionDetailInFaults to receive information about the error. We'll come to this later. The reason that Silverlight can't read the error is that the message has a status 500. Due to the browser networking stack that Silverlight uses under the covers, plugins such as Silverlight don't get access to this message. Hence, we should try to change the status to 200, so that Silverlight can access the error. This change can be brought about by creating a WCF endpoint behavior for Silverlight faults. This will do the conversion from HTTP status 500 to HTTP status 200.We'll create this behavior in a class in a separate project. To do this, create a new class library (not a Silverlight class library, as this library will be used by the web project, not the Silverlight project). Name the new project FaultBehavior.In this new project, we create a class containing the new behavior. The complete code for this behavior can be found at http://msdn.microsoft.com/en-us/library/dd470096(VS.95).aspx as well as in downloads of the book. The most relevant part, which does the status conversion, is shown in the following code: public void BeforeSendReply(ref Message reply, object correlationState) { if (reply.IsFault) { HttpResponseMessageProperty property = new HttpResponseMessageProperty(); // Here the response code is changed to 200. property.StatusCode = System.Net.HttpStatusCode.OK; reply.Properties[HttpResponseMessageProperty.Name] = property; } } Build the class library and reference the project from the SilverlightEmployeeLookup.Web web project.We need to make some configuration changes in the web.config file, so that our service can use the newly created behavior. Change the code in the web.config file as shown in the following code (note that includeExceptionDetailInFaults is set to true in this code): <system.serviceModel> <extensions> <behaviorExtensions> <add name="silverlightFaults" type="FaultBehavior.SilverlightFaultBehavior, FaultBehavior, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </behaviorExtensions> </extensions> <behaviors> <endpointBehaviors> <behavior name="SilverlightFaultBehavior"> <silverlightFaults /> </behavior> </endpointBehaviors> <serviceBehaviors> <behavior name="SilverlightEmployeeLookup.Web .EmployeeLookupServiceBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> <services> <service behaviorConfiguration="SilverlightEmployeeLookup.Web .EmployeeLookupServiceBehavior" name="SilverlightEmployeeLookup.Web .EmployeeLookupService"> <endpoint address="" binding="basicHttpBinding" behaviorConfiguration="SilverlightFaultBehavior" contract="SilverlightEmployeeLookup.Web .IEmployeeLookupService"> <identity> <dns value="localhost"/> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> </system.serviceModel> Now that all the plumbing is done, we can focus on the service again. Let's start by adding a new class called EmployeeFault. This class will contain information on the error that occurred. The following code specifies our own fault type called EmployeeFault. It contains two properties that will be filled with data when an error occurs: public class EmployeeFault { public string Message { get; set; } public string AdditionalInfo { get; set; } } In the contract of the IEmployeeLookupService, we need to add the FaultContract attribute. This is shown in the following code: [ServiceContract] public interface IEmployeeLookupService { [OperationContract] [FaultContract(typeof(EmployeeFault))] List<Employee> RetrieveEmployeesByUserName(string userName); } In the service implementation, we can now create an instance of the EmployeeFault when an exception occurs. In the following code, we check if there are no results and if so, we return an instance of the EmployeeFault that contains information regarding the results: if (results.Count<Employee>() == 0) { EmployeeFault fault = new EmployeeFault(); fault.Message = "No results found."; fault.AdditionalInfo = "Perhaps try a different search term."; throw new FaultException<EmployeeFault>(fault, "Bad search term"); } Build the solution again. We also need to update the service reference, so that the client-side proxy code is recreated. To do this, right-click on the service reference and select Update Service Reference. The generated code in the proxy class will now also include the EmployeeFault.In the callback method of the Silverlight application, we can now check if the result contains an error, and if it does, we can check the type of the returned fault. This fault also includes relevant information that we can use to display to the user: if (e.Error == null) { EmployeesDataGrid.ItemsSource = e.Result; } else if (e.Error is FaultException<EmployeeFault>) { FaultException<EmployeeFault> serviceFault = e.Error as FaultException<EmployeeFault>; ErrorTextBlock.Text = serviceFault.Detail.Message + " " + serviceFault.Detail.AdditionalInfo; } If we run the application now and search for a non-existing employee, we see the information contained in the fault returned by the service, as shown in the following screenshot: How it works... When a service encounters an error, it returns an HTTP status code 500. Silverlight can't receive these responses, because of the browser networking stack. Due to this, when working with services, we often encounter the The remote server returned an error: NotFound error error message. This isn't very helpful, because no information on what really went wrong is included. To make it possible for Silverlight to read service faults, we need to change the status code from 500 to 200. This can be achieved by adding a behavior that will do this for our WCF service. When using this behavior on a service, we also need to make configuration changes in the web.config file of the service project. After this change, service faults are accessible in Silverlight. The fault—a SOAP fault—needs to be translated into managed code. Silverlight 2 could not do this. However, this changed with the introduction of Silverlight 3, and remained the same for Silverlight 4 and 5 as well. This way, error information sent by the service can be translated into a managed exception that can be inspected and used for error-handling purposes. Types of faults There are two types of faults that can be sent by a service—a declared fault and an undeclared fault. Declared faults Declared faults are the type of faults we should use in a production environment. These define a custom type that will be sent back to the client-side application, containing information on the error that occurred. Declared faults occur in operations annotated with the FaultContractAttribute. By annotating an operation with this attribute, we specify that this operation may throw this type of exception, and if it does, the resulting fault instance should be sent to the client. When building the proxy, the custom fault type will be generated on the client side as well. This way, we can build code that will check if the returned error is an instance of the custom fault type. If it is, we can get the error information from the instance. Undeclared faults During development, we often want all the information about an error that occurs. Remember the error message we got earlier in this recipe using Fiddler (in step 3)? Hidden in the response of the service, it was mentioned that we could get more information on the error that occurred by setting IncludeExceptionDetailInFaults to true in the service configuration. This is shown in the following line of code: <serviceDebug includeExceptionDetailInFaults="true"/> Doing this (and thus not using the declared faults), results in the original error being sent over the wire. Silverlight will also be able to pick up this type of fault (assuming we still have the behavior in place that converts status 500 to status 200). The following code can be used to capture undeclared faults: void proxy_RetrieveEmployeesByUserNameCompleted(object sender, SilverlightEmployeeLookup.EmployeeLookupService .RetrieveEmployeesByUserNameCompletedEventArgs e) { if (e.Error == null) { EmployeesDataGrid.ItemsSource = e.Result; } else if (e.Error is FaultException<ExceptionDetail>) { FaultException<ExceptionDetail> serviceFault = e.Error as FaultException<ExceptionDetail>; ErrorTextBlock.Text = serviceFault.Detail.Message; } } If we run this code, the Silverlight application will show the error message, as it was sent when the error occurred on the service. The main reason to favor declared faults for production code is that we don't want to show real service errors to the end users. These might even include sensitive information, such as database passwords.

  

You are currently reading a PREVIEW of this book.

                                                                                                                    

Get instant access to over $1 million worth of books and videos.

  

Start a Free Trial


  
  • Safari Books Online
  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • PrintPrint