Wahlin on .NET

Consuming WCF Services in Silverlight 3

If you've consumed an ASP.NET ASMX Web service before, then you'll feel right at home consuming a WCF service.

In a previous article, I discussed how a Windows Communication Foundation (WCF) service could be created for consumption by a Silverlight client. Using services, you can easily pass data from a server to a client without having to a write a lot of custom code.

In this article, I'll walk you through the steps required to consume a service in Silverlight 3 and mention a few important features to keep in mind along the way. I'll also discuss how to handle asynchronous calls within a Silverlight client.


Consuming a Service
The Silverlight Tools for Visual Studio 2008 provide built-in functionality to create Silverlight-enabled WCF services and consume them without writing a lot of code. Silverlight 3 now supports binary XML which allows data to be transferred from server to client faster than ever. Standard SOAP messages can also be sent if required.

If you've consumed an ASP.NET ASMX Web service before, then you'll feel right at home consuming a WCF service. Visual Studio 2008's Add Service Reference dialog allows you to pick the service and generate a client proxy without writing any code at all. Before generating the proxy, it's important to understand that Silverlight can only call back to the origin server where the Silverlight XAP application file was served from by default.

If you need to call a WCF service (or another service) that's either in a different domain or on a different port, then the service must have a client access policy file named clientaccesspolicy.xml at the root of the server. Silverlight checks for this file to see if it's allowed to make a cross-domain call (additional details on client access policy files can be obtained here).

Listing 1 below shows a sample file:

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from>      
        <domain uri="*"/>
      </allow-from>      
      <grant-to>      
        <resource path="/" include-subpaths="true"/>
      </grant-to>      
    </policy>
  </cross-domain-access>
</access-policy>

Listing 1: A client access policy file can be placed at the root of a server to allow Silverlight clients to call services located in different domains or running on different ports.

To create a WCF service proxy, you'll first need to create a Silverlight application project. Once that's created, you can right-click on the project and select Add Service Reference. You'll be presented with the Add Service Reference dialog, as shown in Figure 1.

Figure 1
[Click on image for larger view.]
Figure 1. The Add Service Reference dialog allows a client proxy to be generated quickly and easily without writing any code.

If the WCF service is located in the same Visual Studio solution, you can click the Discover button. Otherwise, you'll need to type the path to the service's Web Service Description Language (WSDL) file in the Address textbox. Once the service is located, you can give the proxy code a namespace and then click the OK button.

Once the client proxy is created, you can call the service from within a Silverlight application. It's important to remember that all of the networking operations that occur within Silverlight are asynchronous so that the browser doesn't lock up while a service is called. Listing 2 shows an example of using a client proxy to call a service and bind data:

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    //Create service proxy
    WcfService.Service1Client proxy = new WcfService.Service1Client();
    //Wire the proxy to a completed handler to allow the async operation to be handled
    proxy.GetCustomerCompleted += 
      new EventHandler<WcfService.GetCustomerCompletedEventArgs> (
    proxy_GetCustomerCompleted);
    //Call the service asynchronously
    proxy.GetCustomerAsync(id);
}

void proxy_GetCustomerCompleted(object sender, SilverlightApplication1.WcfService.GetCustomerCompletedEventArgs e)
{
    //Bind the returned data to the DataContext
    this.DataContext = e.Result;
}
Listing 2: Using a client proxy object to call a WCF service within Silverlight.

Looking through the code in Listing 2, you can see that the proxy object is first created and then wired to an asynchronous event handler named proxy_GetCustomerCompleted using an event-driven pattern. The service is then called asynchronously by invoking the GetCustomerAsync method. Once the service returns data the callback method is called automatically and the GetCustomerCompletedEventArgs parameter is accessed to retrieve the data. In this example, the data is bound directly to the application's DataContext property. Note that no extra work is required to route the data from the calling thread back to the user interface thread. The proxy handles all of that for you automatically.

Silverlight applications can't call a database directly, but they can access data from a variety of services such as WCF services, ASMX services and other standards-compliant services (to name just a few options).


About the Author

Dan Wahlin (Microsoft MVP for ASP.NET and XML Web Services) is the founder of The Wahlin Group which specializes in .NET and SharePoint onsite, online and video training and consulting solutions. Dan also founded the XML for ASP.NET Developers Web site, which focuses on using ASP.NET, XML, AJAX, Silverlight and Web Services in Microsoft's .NET platform. He's also on the INETA Speaker's Bureau and speaks at conferences and user groups around the world. Dan has written several books on .NET including "Professional Silverlight 2 for ASP.NET Developers," "Professional ASP.NET 3.5 AJAX, ASP.NET 2.0 MVP Hacks and Tips," and "XML for ASP.NET Developers." Read Dan's blog here.

Reader Comments:

Wed, Oct 13, 2010

Thank for the Clear explaination. Mircosoft can take lessons from you.

Sun, Jul 11, 2010 elDiablo

Dude.. this article is so to the point. Excellent! Thank you!

Fri, Feb 26, 2010 JF Payette Montreal

The crossdomain.xml and clientaccesspolicy.xml needs to be at the root of the site, not at the root of the server...

Tue, Nov 17, 2009 Michael Arizona

I'm at my wits end. I have the service running basicHTTP, I have the ASP.NET compatibility and Requirements attributes set, I have the client policy and cross domain policy file at the root of the web, I have all IIS security turned off, I have the database connection still using Windows Security but it asks me for the login credentials so that should be okay for now. However I still get THE EXCEPTION that : An error occurred while trying to make a request to URI '{my service url}'. This could be due to attempting to access a service in a cross-domain way without a proper cross-domain policy in place, or a policy that is unsuitable for SOAP services This isn't as easy as your article makes it sound. Any suggestions?

Thu, Oct 29, 2009 Dhawal NY

I get the following exception when I try to add service reference: The HTML document does not contain Web service discovery information.
Metadata contains a reference that cannot be resolved: 'http://localhost:53037/UI/WebServices/ProrationService.svc'.
The content type text/html; charset=utf-8 of the response message does not match the content type of the binding (application/soap+xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 1024 bytes of the response were: '

Fri, Aug 21, 2009

Thank you!

Thu, Jun 25, 2009 Noel Fort Worth, TX

How could you implement the code in "MainPage_Loaded()" so that it is testable? Doesn't putting that code in the code-behind make it untestable?

Add Your Comments Now:

Your Name:(optional)
Your Email:(optional)
Your Location:(optional)
Comment:
Please type the letters/numbers you see above