Data Points

Cloud Gazing From Silverlight 2

John Papa

Code download available at:MSDN Code Gallery(151 KB)

This column is based on a prerelease version of Silverlight 2. All information is subject to change.

How are Web services handled in Silverlight 2?
How is data passed from services to Silverlight 2?
How is data consumed from ASMX Web services?
How is data consumed from WCF Web services?
How is data received from a REST Web service?
How do I query XML?
Do I need to worry about cross-domain policies when I'm accessing third-party Web services?

Developers who use Silverlight have their heads in the clouds, but can you blame them? Obtaining data for rich Internet applications (RIA) from various types of Web services is growing in prominence. Silverlight apps can communicate with ASMX Web services, Windows Communication Foundation (WCF) Web services, Representational State Transfer (REST) services, and plain old XML (POX) services. Whether these services are supplied by third parties or they are custom services hosted on the same server where the Silverlight app resides, Silverlight can request data, consume the data and pass data back and forth between the Silverlight client application and these types of Web services.

Based on questions I have been receiving, it's clear that developers want to know more about calling services from Silverlight 2 applications, the differences between using WCF, ASMX, and REST services, and about how to consume the data that these services expose. In this installment of Data Points, I will address some of these questions and demonstrate how to deal with data services.

There are plenty of third-party Web services that offer everything from access to their APIs to storage of client-specific and custom data. I'll discuss the consuming and passing of data between Silverlight applications and the cloud of services. I'll also show how data can be consumed using several XML parsing libraries. For example, XML documents can be opened and iterated through or XML can be queried using LINQ to XML. Services such as Flickr, Amazon, Twitter, and Live Search all expose APIs that can be communicated with through various Web service techniques. Here I'll discuss some of these APIs and show how you can communicate with them using REST- and SOAP-based services. All examples in this column are available in both C# and Visual Basic and can be downloaded from the MSDN MagazineWeb site.

How are Web services handled in Silverlight 2?

This is a good question I like to start with, as it sets the basis for understanding why so many developers are using Silverlight 2 to interact with Web services. Silverlight 1.x did not support code based on the Microsoft .NET Framework, nor were there any .NET controls. Silverlight 2 introduced a lot of full-featured functionality that blew away previous limitations. Silverlight 2 allows you to write code in C# or Visual Basic and leverage all your existing experience against the .NET CLR. While the libraries included in Silverlight are a subset of the overall .NET libraries, there is plenty of functionality in Silverlight 2. For example, the WebClient and HttpWeb­Request classes are accessible from Silverlight and can be used to interact with Web-based services by calling URIs. The data can then be consumed using XmlReader objects or through LINQ to XML.

Silverlight 2 added a large set of features for passing data between services. The following features are all new to Silverlight 2:

  • SOAP-based Web services can be accessed through proxy classes.
  • REST-based Web service can be accessed.
  • ADO.NET Data Services, a REST-based service that allows remote LINQ queries, can be accessed.
  • Results can be retrieved using both JavaScript Object Notation (JSON) and XML from Web services.
  • Duplex communications are supported through WCF (using server push).
  • Cross-domain access is supported using either the client­accesspolicy.xml file or the crossdomain.xml file.
  • Cross-domain network support is available using HTTP and Sockets.
  • Web service calls are initiated asynchronously.

How is data passed from services to Silverlight 2?

Figure 1 shows several types of services that Silverlight 2 can access. Data can be passed between Silverlight 2 and these services as XML, JSON, or as scalar values. SOAP-based services allow themselves to be described, which, in turn, allows the data that they expose to be described. A Silverlight 2 client can access the data from SOAP-based services by creating a proxy to the service, which generates a class description for the exposed classes.

Figure 1 Accessing Services from Silverlight

SOAP-based services describe themselves so the Silverlight 2 client application can pass data to and from the services using any exposed entity. Both ASMX Web services and WCF Web services allow entities to be included as part of their contract with a client reference. The client reference generates a proxy class on the client that contains a definition for the exposed classes and the service methods (all service methods are converted to asynchronous calls for Silverlight 2 clients).

Services that do not describe themselves, such as POX and REST-based services, allow a client application to call their service methods and retrieve data in scalar format, as XML, or as JSON. These types of services do not expose Web Services Description Language (WSDL) and therefore the client application does not generate a proxy class for them. The services are queried using a URI through classes like the WebClient or the HttpWeb­Request classes.

How is data consumed from ASMX Web services?

Like ASMX services, SOAP-based WCF Web services describe themselves through WSDL. If the client adds a reference to the service, it will generate a client proxy to the service, allowing the client to pass data with the service in their native form (that is, as classes). For example, a Dog entity may be returned from a SOAP-based Web service (ASMX or WCF) to a Silverlight 2 client. The Silverlight 2 client can then create an instance of the Dog class. Methods are exposed to clients in ASMX Web services by decorating the public service methods with the WebMethod attribute and decorating the ASMX Web service class with the WebService attribute. Figure 2 illustrates this in C#.

[WebService(Namespace = "https://www.microsoft.com/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [System.ComponentModel.ToolboxItem(false)] public class TestService : System.Web.Services.WebService { [WebMethod] public List GetDogList() { return new List {new Dog {Name = "Spot", Age = 10}, new Dog {Name = "Kadi", Age = 13}}; } } [Serializable] public class Dog { public string Name { get; set; } public int Age { get; set; } }

This example shows the .NET-targeted code in a Silverlight 2 client app that generates the proxy to the SOAP service. An event handler is assigned to the GetDogListCompleted event followed by the asynchronous call to the service. All calls to Web services from Silverlight 2 are made asynchronously to prevent the services from tying up the UI thread and freezing the user. Here's the event handler receiving the results as an array of the entity type:

TestServiceSoapClient proxy = new TestServiceSoapClient(); proxy.GetDogListCompleted += new EventHandler(proxy_ GetDogListCompleted); proxy.GetDogListAsync();

Here's the C# code for receiving the entities:

void proxy_GetDogListCompleted(object sender, GetDogListCompletedEventArgs e) { Dog[] list = e.Result; }

How is data consumed from WCF Web services?

Like ASMX Web services, WCF Web services describe themselves through WSDL. WCF Web services expose their services and the data that they can pass using a series of attributes. This means that the client applications can reference a WCF Web service and generate a proxy class to interact with the services and their exposed data contracts.

WCF defines a data contract as any custom .NET type that can be returned from a WCF Web service. These types are decorated with the DataContract attribute. Properties of the class must be decorated with the DataMember attribute. Figure 3 shows a sample class decorated with the appropriate attributes.

[DataContract] public class Employee { int _employeeID; string _firstName; string _lastName; string _title; DateTime _hireDate; byte[] _photo; [DataMember] public int EmployeeID { get { return _employeeID; } set { _employeeID = value; } } [DataMember] public string FirstName { get { return _firstName; } set {_firstName = value; } } [DataMember] public string LastName { get { return _lastName; } set { _lastName = value; } } }

Once the data formats have been defined for the WCF Web service using the DataContract and DataMember attributes, the service and its service methods must be set up. The services are decorated with the ServiceContract attribute and its methods are decorated with the OperationContract attributes. If desired, the service can implement an interface, in which case the interface (and not the service class) and its interface methods are decorated with the attributes. Here is the IEmployeeService and its members decorated with these attributes:

[ServiceContract(Namespace = "")] public interface IEmployeeService { [OperationContract] List FindEmployees(); }

Note here that Silverlight 2 applications can only consume SOAP-based WCF services that use basicHttpBinding.

A Silverlight 2 client application can add a service reference to this service, which creates a proxy class. The service can then be called asynchronously, just like with an ASMX Web service, and a handler can help capture the results. The code to implement the proxy and the completed event handler use exactly the same format, as shown earlier for ASMX Web services. Once the data is returned as a List the entities can be iterated, data bound, modified, and passed back through a service method if required.

How is data received from a REST Web service?

REST services can be called through a URI using a query string with parameter. It can be called from Silverlight 2 using the Web­Client class or the HttpWebRequest. Making calls through WebClient is simpler, but HttpWebRequest offers more control over how the requests are made. Also, WebClient returns on the UI thread while HttWebRequest returns on the background thread. HttpWeb­Request's callback must use the Dispatcher to interact with the UI. The following code shows the System.Net.WebClient retrieving recent stories from the Digg service:

string baseUri = "https://services.digg.com/stories/topic"; string topic = txtTopic.Text; string appKey = "http%3A%2F%2Fwww.microsoft.com"; int count = int.Parse(txtTopicCount.Text); string url = String.Format("{0}/{1}?appkey={2}&count={3}", baseUri, topic, appKey, count); WebClient svc = new WebClient(); svc.DownloadStringAsync(new Uri(url));

Here the data is returned from the WebClient call as raw XML which can then be parsed or queried. If the REST service has parameters, they can be passed as part of the querystring.

How do I query XML?

When a REST-based or POX service is invoked, it can return data as XML (REST services can also return data as JSON). The XML can then be parsed using a number of XML parsing libraries. However, the XML can also be queried using LINQ to XML, which offers a rich query interface to extract data from XML structures without having to resort to iterating through complex XML hierarchies.

The REST service in the example you just saw returns raw XML from the Digg service that shows the most recent stories. This XML can then be parsed using an XML library or it can be queried using LINQ to XML, as shown in Figure 4. The X­Document class's Parse method can consume the XML from the REST service shown earlier. The XML can then be queried using LINQ to XML. The code shown in Figure 4 demonstrates the LINQ to XML syntax for querying the XML shown in Figure 5.

XDocument xml = XDocument.Parse(rawXml); var storiesQuery = from story in xml.Descendants("story") select new DiggStory { Id = (int)story.Attribute("id"), Title = ((string)story.Element("title")).Trim(), Description = ((string)story.Element("description")).Trim(), ThumbNail = (story.Element("thumbnail") == null ? string.Empty : story.Element("thumbnail").Attribute("src").Value), Link = new Uri((string)story.Attribute("link")), DiggCount = (int)story.Attribute("diggs") };

Microsoft has released SQL Server 2005 recently as an upgrade to the popular SQL Server database. Microsoft's virtual receptionist ... ...

The query starts in the stories/story hierarchy and queries all of the story elements. There is no namespace specified for this XML; however, I left the namespace variable (ns) in place to show how to prefix all XML paths in case the results do refer to a namespace. Driving story elements is accomplished using the XDocument instance's Descendants method. The story element's properties can then be retrieved using the story variable and its Element method. For example, the following line of code grabs the value of the title element from the story element and sets it into a property named Title:

Title = ((string)story.Element("title")).Trim()

The LINQ to XML query can return the results through a projection or into a class structure. The code shown in Figure 4 creates a DiggStory class to store the results of each status element retrieved from the XML data. The DiggStory class that the developer has defined, where all of the properties (for simplicity) have been defined as string. LINQ to XML could include other aspects such as ordering or filtering criteria, too, if needed.

Using LINQ to XML to query and parse XML data from REST or POX services allows developers to leverage their existing knowledge of other LINQ forms since the syntax is very similar across them all. Perhaps the biggest benefit of using LINQ to XML to parse the data is the power and simplicity of querying the data without having to resort to iterating through elements in nested foreach loops.

Do I need to worry about cross-domain policies when I'm accessing third-party Web services?

When a Silverlight 2 application makes a Web service call, the call is precluded by a check on the Web service's server for a cross-domain policy file. This check is done when the Silverlight application is hosted on a different domain than the Web service. For example, Figure 6 shows that a Silverlight 2 application hosted on the Web server johnpapa.net can call a Web service also on johnpapa.net since they are on the same domain. However if the Silverlight 2 client hosted from johnpapa.net wants to call a Web service hosted on microsoft.com, Silverlight 2 checks to make sure the cross-domain policy file on microsoft.com exists and allows the call.

Figure 6 Cross-Domain Checking

For more information on Cross-Domain Policies, see my September 2008 column, "Service-Driven Apps With Silverlight 2 And WCF". In that column, I discuss the file formats and how the policies work.

Whether the call is made to a WCF Web service, a REST-based service or any type of Web service, Silverlight 2 only allows cross-domain calls to occur if the target Web server allows the call through its cross-domain policy file. For example, the code you saw earlier

string baseUri = "https://services.digg.com/stories/topic"; string topic = txtTopic.Text; string appKey = "http%3A%2F%2Fwww.microsoft.com"; int count = int.Parse(txtTopicCount.Text); string url = String.Format("{0}/{1}?appkey={2}&count={3}", baseUri, topic, appKey, count); WebClient svc = new WebClient(); svc.DownloadStringAsync(new Uri(url));

invokes the Digg Web services to access the most recent stories. Digg includes a cross-domain policy file using the Flash format at the location https://services.digg.com/crossdomain.xml.

The contents of this Digg cross-domain file at the time of this writing are shown here:

Notice that the Digg cross-domain file allows calls from any domain. However, this could be restricted, just like the Twitter cross-domain file enforces restrictions. Notice the Twitter cross-domain file in the following:

The cross-domain file for Twitter restricts calls to those that originate from the *.twitter.com domain. Your Silverlight 2 application likely is not hosted on the twitter.com domain, so a Silverlight client cannot make a Web service request to Twitter's REST-based services directly. This is where a proxy service can help you out. For example, you could create a WCF Web service hosted on your domain that calls the Twitter.com Web services. The calls from your WCF services relay the information between the Silverlight application and Twitter. The WCF service running on the server does not suffer the cross-domain policy file restrictions as a Silverlight client does, so it can make the calls. Another option is to relay the calls through a service such as Popfly or Yahoo's cloud services.

Send your questions and comments for John to mmdata@microsoft.com.

John Papa (johnpapa.net) is a Senior Consultant with ASPSOFT (aspsoft.com) and a baseball fan who spends summer nights rooting for the Yankees with his family. John, a C# MVP and INETA speaker, has authored several books and is now working on his latest, Data-Driven Services with Silverlight 2. He often speaks at conferences such as DevConnections and VSLive.