Chapter 6. Networking and communications – Silverlight 2 in Action, illustrated edition

Chapter 6. Networking and communications

This chapter covers

Chapter 5 introduced you to the convenient data-binding mechanisms available within Silverlight. As an added luxury, you got an overview of Silverlight’s impressive LINQ API, which gives you a way to abstract your data from its type, making it easier to work with. This feature is valuable because data can come in a variety of formats from a multitude of sources; you can use LINQ to corral these data formats. Working with data is essential to most applications, but you have to get the data into your application somehow. This is where networking and communications come in.

Silverlight provides us with numerous methods to get to data hosted on other systems, from complex web services to a simple XML document. The networking and communications features of Silverlight enable you to send and receive data with a variety of technologies including SOAP services, XML, JSON, RSS, Atom, and even sockets. We’re going to talk about some basic principles, how to connect to various services, and how to parse the data once received, as well as a couple ways to enable push communications. Let’s get started with the basics.

6.1. Trust, security, and browser limitations

You must consider several basic concepts when using the communication APIs in Silverlight. These concepts—trust, security, and the limitations of the browser—apply to all methods of communication discussed in this chapter.

Silverlight executes from the confines of the client browser. Because of this, you have to retrieve data differently than the way you may be used to with a server-side technology such as ASP.NET. For example, you can’t directly connect to a database without using something as a proxy, most commonly a web service. Although this method of communicating resembles that used by AJAX, there are significant differences.

Imagine you’re building a Silverlight application that allows users to add items to a shopping cart. As soon as users add an item to their cart, you want to reserve it for them. Because Silverlight executes in the browser, you can’t just call the database and mark the item as reserved. You need something acting as a proxy to the database. Throughout this chapter, we’ll discuss methods of connecting to remote services to accomplish tasks like this. For now, let’s move to the first basic concept: trust.

6.1.1. It’s all a matter of trust

The concept of trust applies to cross-domain access. If your application is hosted at http://silverlightinaction.com and you’re attempting to access a web service hosted at http://silverlight.net, the request is made cross-domain. In the case of cross-domain access, it isn’t a matter of whom your application trusts, but of who trusts your application. In the vein of increased security, Silverlight, like Flash before it, has restricted client applications to connecting only to the same website that hosts the application itself. For those familiar with web services, this seems quite counterproductive, so the Silverlight team also worked in an exemption that requires the involvement of the server hosting the web service. The administrator of the web server can create the policy file to give access to only the resources he wants exposed to the requesting domains he trusts. A simple XML file is added that tells the Silverlight application what it has access to on the foreign server.

The clientaccesspolicy.xml defines these policies; it needs to be placed at the root of the domain hosting any web service that’s allowed to be accessed from a different domain. Even if there’s a valid policy file, if it’s located anywhere other than the root of the hosting domain, your application won’t find it, and the request will fail. If the file is in place and has the proper attributes, your application is considered trusted, and the call will return as expected. So, what does a properly formatted policy file look like? Let’s look at snippet 6.1.

Snippet 6.1. XML: Base clientaccesspolicy.xml for WebClient or HTTP access
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="*"/>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>

This snippet shows the minimum needed in a clientaccesspolicy.xml to allow HTTP access to all web services hosted on the current domain. If you want to have different rights for different services or to allow for socket access to the server, you can make additions to that file. Sockets are described in section 6.4. The example listed in snippet 6.1 is as open as possible—requests from any domain can access any resource in the host domain, and host headers are allowed. Table 6.1 shows the elements and attributes that make up a clientaccesspolicy.xml file. Attributes are shown after the element they apply to.

Table 6.1. Elements and attributes allowed in clientaccesspolicy.xml

Element/Attribute

Required

Description

access-policy Yes Root element for the policy file.
cross-domain-policy Yes Container for one or more policy elements.
policy Yes Defines rules for a single domain or a group of domains.
allow-from Yes Container for permitted domains. If it contains no domain elements, no cross-domain access is granted.
  • http-request-headers
No Defines which headers are allowed to be sent to the web services hosted at the current domain. If absent, no headers are allowed.
domain Yes Defines domains affected by the policy element in which the domain is a child.
  • uri
Yes Specifies the exact domain allowed for the current policy.
grant-to Yes Container for one or more resource elements.
resource Yes Required for WebClient or HttpWebRequest classes. Defines server resources affected by the current policy.
  • Path
Yes Required for WebClient or HttpWebRequest classes. Identifies individual files or services allowed or denied by the current policy. Format is a URI relative to the root of the domain.
  • include-subpaths
No Optional for WebClient or HttpWebRequest classes. If absent, subfolder access is denied.
socket-resource Yes Required for socket access. Defines socket resources affected by the current policy.
  • Port
Yes Required for Socket access. Defines a port or range of ports, which must fall between 4502 and 4534, affected by the current policy.
  • Protocol
Yes Required for socket access. Defines what protocols are allowed under the current policy. The only protocol currently allowed is TCP.

Now we know that you’re anxious to see how to connect to data from within your application, but we need to create a solid foundation on which to build service access. You can make the policy file as open or as restrictive as you desire. By changing the domain element, you can limit access to a single domain. You can also add multiple policy elements to apply different rules to requests from different domains, as shown in snippet 6.2. Two separate policies are defined in this snippet. The first allows any request coming from sometrusteddomain.com to have unrestricted access to the entire application; the second forces requests from any other domain to be restricted to the API folder and to have HTTP headers denied.

Snippet 6.2. XML: More restrictive client access policy
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="http://sometrusteddomain.com"/>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
<policy>
<allow-from>
<domain uri="*"/>
</allow-from>
<grant-to>
<resource path="/api"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>

The elements and attributes shown apply for connecting to any HTTP-based resource. Modifications are needed if you’re using TCP sockets, which are described in section 6.4.

Even if this file isn’t in place, you might still be in luck. Silverlight will also use policy files created for use with Adobe Flash, known as crossdomain.xml files, for cross-domain access. There are two restrictions when using a crossdomain.xml file:

  • It may only be used for WebClient (WebClient usage is discussed in chapter 11, section 11.4) or HttpWebRequest or service reference proxy access. Socket access isn’t permitted.
  • The entire domain must be allowed access for Silverlight to use a crossdomain.xml file. Silverlight doesn’t respect the advanced properties of crossdomain.xml.

If the domain hosting the web service you’re calling has either the clientaccesspolicy.xml or the crossdomain.xml with the correct attributes in place, it’s considered trusted and will return a result. It’s necessary to have an outside source trust your application, but should you trust everyone else? Let’s look at a few ways to ensure that your application is as safe and secure as possible.

6.1.2. Making your application secure

Just as you put a valid policy file in place for security reasons, you can take other steps to make your application more secure. In this section, we’ll briefly discuss data integrity, using HTTPS, and attaching HTTP headers to your request.

Data Integrity

Never trust that your data source is going to return pure, clean data every time. It’s possible that, either purposefully or as a result of an error on the part of the service creator, harmful data may be returned to your application. You should always take steps to validate that the data you receive is compatible with the use you’re intending.

HTTPS

Anytime you’re passing sensitive data, you should use Hypertext Transfer Protocol over Secure Sockets Layer (HTTPS), which encrypts the message to prevent eavesdropping. Silverlight doesn’t allow cross-schema access to services, though—both the Silverlight application and the web service have to be using either HTTP or HTTPS, but can’t mix the two.

Cookies

Because Silverlight uses the browser’s networking stack, cookies for the current domain also get added to requests from your Silverlight application. This is good if you’re hosting your Silverlight in an authenticated application that uses tokens stored in a cookie. In this case, the token also authenticates the requests originating from your Silverlight application, ensuring that you can access the resources you’re authorized for.

One potential problem using this method is that once a user has a cookie-based token for a given domain, any Silverlight request to that domain contains the auth cookie, even if the request is from a different Silverlight application than the one you intend. Another issue is that this method of authentication relies on the client browser having session cookies enabled—which isn’t always true. Trust and security are important for any application; before we move on to the meat of accessing data, let’s make sure the browser itself is capable of doing everything we’re asking of it. This question brings us to the next basic concept: limitations of the browser.

6.1.3. Limitations of the browser

A few limitations apply to Silverlight due to its use of the networking API of the host browser. These limitations have affected browsers for years, and now they carry over into Silverlight as well. We’ve already discussed the client-side nature of Silverlight and how that affects data access, so now we’re going to talk about two similar limitations: connection limits and asynchronous calls.

Connection Count Limit

The number of connections to a single subdomain (for example, http://www.microsoft.com and http://msdn.microsoft.com are different subdomains) is limited in most browsers to two concurrent calls. This limit is being increased in Internet Explorer 8 to six concurrent connections; but, because Silverlight runs in multiple browsers, you should still be aware of it. Because Silverlight uses the browser’s networking stack for its communications, it’s bound by the same limits. You may need a combination of resources or other approaches to ensure it doesn’t create unnecessary delays in your application. Keep in mind that the browser may be loading objects outside the scope of the Silverlight application as well, such as stylesheets, images, or JavaScript files. All these objects count toward the two concurrent connection limit. You should keep this fact in mind, particularly when performing duplex communication as described in section 6.4.

Asynchronous Only

All communication from within the Silverlight application is done asynchronously because of the Silverlight 2 development team’s decision to use the browser’s networking API. The most common way of making asynchronous calls requires a few simple steps, which we’ll detail in the following section. Typically, all you need to do is create an event handler to be executed when the call returns. If you want to create a more synchronous experience for your application, you can enable some kind of blocking element, such as a download process animation, when the call begins and then remove it once the request has returned its data. We’ll discuss how to create animations in chapter 9.

Note that the asynchronous behavior can occur on multiple threads, a fact that can cause trouble when you aren’t aware of it. In section 6.2.2, we’ll point out where you need to be careful with which thread you’re on and show you a technique to avoid trouble. Now that we have the basics out of the way, let’s get to the point of this chapter: connecting to data sources.

6.2. Connecting to data sources

Nearly every application built today, even using Silverlight, needs data to accomplish its goal. In chapter 5 you saw how to take data and bind it to properties on controls, but where does that data come from? Because Silverlight executes from the client browser, it doesn’t have direct access to a database as do server-based technologies such as ASP.NET or PHP. To get access to the data from within Silverlight, you need to use a proxy of some sort, typically a web service. A web service is a resource made available to clients using a defined protocol. Most web services fall into two categories: SOAP and REST. We’ll explain these popular formats, and how to use them, in this section.

6.2.1. Using SOAP services

When you think of a classic web service, you’re thinking about Simple Object Access Protocol, or SOAP. SOAP services follow a strict protocol that defines the format in which messages are passed back and forth. Silverlight has great support for SOAP services but currently supports only the SOAP 1.1 protocol. Using SOAP services in Silverlight allows for both the simplest implementation and most powerful data transfer mechanism of any service type through the use of the Service Reference. Over the next few pages, you’ll create a proxy object for the service, call a method on it, and download the results. Once you’ve created and used a proxy to connect to a SOAP service, you’ll be amazed at how simple yet powerful this capability is.

Service References

The easiest way to connect to a service is through a Service Reference proxy. If the web service you’re connecting to supports Web Services Description Language (WSDL) or another discovery protocol, Visual Studio can read that information and create a proxy in your application for you. Creating a service reference in your Silverlight application takes three simple steps:

  1. In Visual Studio 2008, right-click your Silverlight project and choose Add Service Reference…
  2. This brings up the Add Service Reference dialog box. On this form you can either type in the URI of the service you wish to connect to and click the Go button or, if the services are part of the same solution, you can click the Discover button. Either option tells Visual Studio to poll the chosen location for available services and analyzes their signatures. Once the services have been displayed you can open them and look at what methods are available on each service. You can then enter a Namespace that you want to refer to the service by into the textbox and then click OK to create the proxy.
  3. You can modify more advanced settings either by clicking the Advanced button on the previous dialog or by right-clicking the service reference and selecting Configure Service Reference. One particularly useful capability of this form is the ability to change the collection types returned by the service. The default collection type can vary depending on the service you’re connecting to, but you can also change it to use other collection types, even generics.

When the service reference is created, Visual Studio also adds references to the System.Runtime.Serialization and System.ServiceModel assemblies. These are used by Silverlight in connecting to web services and in serializing and deserializing the SOAP message. Once you have your service reference created, it’s quite easy to use for both sending and receiving data. First, let’s talk about calling SOAP services using the service reference just created.

Receiving Data with the Proxy

Connecting to and downloading data from a simple SOAP service is simple. You need to add two Using statements to your page, one for System.ServiceModel and another for System.ServiceModel.Channels. Next, you need to create a proxy to the service using the service reference created in the previous section. Then, you add an event handler to catch the return of the asynchronous call to the exposed method on the service. These steps are demonstrated in snippet 6.3.

Snippet 6.3. C# XAML Result: Calling a simple SOAP service

This snippet shows the entire process of creating the proxy, adding an event handler, calling an asynchronous method, and handling the results. The SOAP service you’re connecting to exposes a method, called GetTime, which accepts no input properties and outputs a DateTime of the current time. You first create a Binding of type BasicHttpBinding . This tells the proxy that the service you’re connecting to uses SOAP 1.1. The default constructor for BasicHttpBinding creates a Binding with no security. An optional parameter on the constructor accepts a BasicHttpSecurityMode, which allows you to tell the binding how to secure itself; for example, you can tell the binding to use SSL when transmitting the SOAP message. You also create an EndpointAddress that points to the URI of the service you’re about to call. Finally, you create the proxy using the Service Reference created earlier and pass into it the binding BasicHttpBinding and the initialized EndpointAddress objects.

Next, you need to add an event handler to be called when your asynchronous method call returns. You can do this by using the Visual Studio shortcut of pressing the + key, then the = key, then pressing Tab twice after selecting the GetTime-Completed event on your proxy . Using this shortcut automatically finishes the event handler declaration for you and creates a stubbed method as the event handler. Finally, you call the GetTimeAsync() method on the proxy to begin the asynchronous call to the service . IntelliSense will show you a [webmethod]Completed event and a [webmethod]Async() method for each method exposed by the SOAP service. When you created the Service Reference in the previous step, Visual Studio queried the service to see what methods were available and made proxy methods for each of them.

Once the service returns, the method declared as the event handler, proxy_GetTimeCompleted, gets called with the results. Because the method is outputting the results as a Datetime object, you can convert it to a string using standard .NET conversion methods , which you can assign to the Text property of a TextBlock. The only other task to perform in the return method is to close out the connection using the CloseAsync() method on the proxy. Garbage collection will technically come through and close any old connections, but it’s good programming practice to close any connection when you’re done using it.

And that’s all there is to it—you’ve now connected to a SOAP service, called a method on it, and displayed the results. Sending data to a SOAP service is just as easy.

Sending Data using the Proxy

If you’re thinking that all you need to do to send data to a SOAP service using a service reference is to include a parameter in the method call, you’re right. Let’s look at snippet 6.4 for an example.

Snippet 6.4. C# XAML Result: Sending data to SOAP service

<TextBlock x:Name="txResults" Grid.Row="0" Grid.RowSpan="2"
Grid.Column="0" Height="Auto" Width="Auto" Text=""
TextWrapping="Wrap" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Margin="5"/>
<Button x:Name="btnTime" Click="btnTime_Click" Content="Get Time"
Grid.Row="0" Grid.Column="1" Height="33" Width="90"/>
<Button x:Name="btnString" Click="btnString_Click"
Content="Get String" Grid.Row="1" Grid.Column="1"
Height="33" Width="90"/>

private void btnString_Click(object sender, RoutedEventArgs e)
{
Binding myBinding = new BasicHttpBinding();
EndpointAddress myEndpoint = new
EndpointAddress("http://localhost:55905/SampleAsmx.asmx");

SilverService.SampleAsmxSoapClient proxy = new
SilverService.SampleAsmxSoapClient(myBinding, myEndpoint);

proxy.GetCoolTextCompleted +=
new EventHandler<SilverService.GetCoolTextCompletedEventArgs>(
proxy_GetCoolTextCompleted);
proxy.GetCoolTextAsync(1);
}

void proxy_GetCoolTextCompleted(object sender,
SilverService.GetCoolTextCompletedEventArgs e)
{
txResults.Text = e.Result;
(sender as SilverService.SampleAsmxSoapClient).CloseAsync();
}

This snippet shows the sending of a single int as a parameter to the GetCoolText method on the web service . Other than that one addition, it’s exactly like the previous example in snippet 6.3. This approach is fine for sending a simple data value, but what about complex data types? Still no problem.

Using Complex Data Types

Sending and receiving complex data types over a SOAP service is also a simple matter. Another task accomplished when you created the Service Reference is that the object signature for any objects used in the SOAP message was analyzed and a proxy was made for those as well. With this proxy, you can instantiate objects of that type in your application (snippet 6.5).

Snippet 6.5. C# Service: Using complex data types with SOAP service

This snippet shows how you can use complex data types on SOAP services from within your Silverlight application. As you can see in the web method declaration, the method SetSomething expects two parameters: an int and a WsUser . WsUser is made up of three properties . Note that both the web method and the WsUser class are part of the ASMX web service, not the Silverlight application.

Now let’s use WsUser in our application. Because WsUser is a return type on a method, its type exists on the proxy for you to use. In this example, you create an instance of the WsUser class and fill its properties . Then you add the instance of the object as a parameter on the asynchronous method call, SetSomethingAsync() .

That’s all it takes to use SOAP services in Silverlight. Silverlight isn’t limited to SOAP services. Next, we’ll discuss consuming REST services through Silverlight, a topic which opens up a whole new arena of data providers.

6.2.2. Time for a REST

Representational State Transfer, or REST, means several things; in this case, it refers to the approach of making services accessible through a set of simple URIs and HTTP verbs. Before the days of web services and AJAX and Silverlight, everything on the web was RESTful, meaning that all traffic over HTTP used one of the HTTP verbs to define its purpose. Over the years, the use of these verbs dwindled down to nearly all traffic using only the GET and POST verbs for requesting a page and submitting form data respectively. Recently there has been a trend toward moving simple web services to a simpler framework.

Many web service providers use the term REST to mean any service that isn’t SOAP. The main thing to realize is that the URI, and possibly the HTTP verb, may change depending on the action being performed. Typically, a creator of RESTful services will try to follow an intuitive structure where the URI first contains a type followed by an instance. For example, a URI with the structure http://www.arestfuldomain.com/Users might return an array of user records, whereas the URI http://www.arestfuldomain.com/Users/JohnSmith might return a single user record for John Smith. This isn’t a rule of REST services; it’s more of a guideline.

Silverlight currently supports only the GET and POST verbs. This is another limitation of using the browser’s networking stack. Luckily, because this is a common browser limitation, most service creators are aware of it and try to use those two verbs.

In the previous section, you saw how to use service references to create proxies to SOAP services. Consuming a REST service takes a little more work on the side of the Silverlight developer. Silverlight nicely handles calling RESTful services through the HttpWebRequest object that you’re already familiar with. In this section, we’ll show you how to use this class to read data from and send data to a RESTful service; the asynchronous nature of these calls can cause problems accessing the UI, so let’s solve that first.

Bypassing Threading Problems

The asynchronous nature of Silverlight web service calls can create threading problems for the developer. When you’re dealing with Service-Reference-generated proxies, threading isn’t an issue; when you’re creating the connection yourself, you have to deal with this part as well. When you attempt to access UI elements directly within call-back methods, you get a threading exception. You deal with this by creating a class-level variable of type SynchronizationContext, which gives you a handle back to the correct thread to do UI updates (snippet 6.6).

Snippet 6.6. C#: Threading the UI
private SynchronizationContext UIThread;       
private void btnSingleXml_Click(object sender, RoutedEventArgs e)
{
UIThread = SynchronizationContext.Current;
...
request.BeginGetResponse(SingleXmlCallBack, request);
}

private void SingleXmlCallBack(IAsyncResult result)
{
...
UIThread.Post(UpdateUiText, responseStream);
}

private void UpdateUiText(object stream)
{
...
}

The first thing you do in this snippet is create a class variable of type SynchronizationContext . Scoping it at the class level means you’ll have access to it no matter where you are in the process. Next, in the method that starts the request (we’ll detail the request in the next section), you assign a reference to the current thread to the variable previously created . Then, in the callback method, you call the Post method on the SynchronizationContext variable, which has two parameters. The first parameter accepts a method to do the UI update with, and the second accepts an object. In this case, it’s simplest to send the entire response stream as the second parameter . Finally, in the method called by the Post method, you can cast the received object into a Stream and perform whatever UI updates you need . You don’t need to pass the entire response stream to the method that updates the UI —you can send any object. It’s our personal preference to let the update method also do any deserialization; by using this technique, we ensure that our UI updates will succeed.

As you can see, as long as you know how to get back to the UI thread, there isn’t a problem here. Now let’s GET to the meat of REST services.

Getting from Rest Services

In relation to Silverlight, although REST may dictate the method in which a resource is accessed, it doesn’t dictate the format of the data received. The most common ways to return data from a RESTful web service are POX and JSON. We’ll discuss how to consume both POX and JSON in section 6.3.

The basics of calling a REST-based web service from Silverlight involve creating an HttpWebRequest object, setting its destination URI, and calling it asynchronously (snippet 6.7).

Snippet 6.7. C#: Getting data from a REST service

In this snippet, you’re making a simple request to a RESTful web service. There are three necessary steps when making a GET request, all of which are demonstrated at :

  1. Create a Uri object and initialize it with the path and, optionally, the UriKind.
  2. Create an HttpWebRequest object for the Uri.
  3. Call BeginGetResponse on your HttpWebRequest object and pass it the name of a callback method, as well as the HttpWebRequest itself.

The BeginGetResponse method initiates the call to the service and registers the passed-in method as a callback method. When the response returns, that method will be called with the current HttpWebRequest being passed to it as type IAsyncResult.

In the callback method, the first thing is to cast the AsyncState of the IAsync-Result into an HttpWebRequest object. In the next statement, you call the EndGet_Response method on the request object to both end the connection and return an HttpWebResponse object. Finally, you call the GetResponseStream method of the HttpWebResponse object to get the Stream, the response to your web service call . We’ll cover deserializing the Stream into useful data in section 6.3.

Posting to Rest Services

Most RESTful services use GET to retrieve data and POST to send it. Because the default HTTP verb used when using HttpWebRequest is GET, there are a few things you need to do differently when you want to perform a POST. Snippet 6.8 shows the process of sending data to a REST service.

Snippet 6.8. C#: POSTing data to a REST service
private void btnTest_Click(object sender, RoutedEventArgs e)
{
UIThread = SynchronizationContext.Current;

Uri path = new
Uri("http://www.silverlightinaction.com/Authors.svc/Update/Stockton",
UriKind.Absolute);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(path);
request.Method = "POST";
request.ContentType = "application/xml";

request.BeginGetRequestStream(AddPayload, request);
}

private void AddPayload(IAsyncResult result)
{
HttpWebRequest request = (HttpWebRequest) result.AsyncState;
StreamWriter dataWriter =
new StreamWriter(request.EndGetRequestStream(result));
dataWriter.Write("<?xml version=\"1.0\"?><Author><FirstName>Bob" +
"</FirstName><LastName>Smith</LastName></Author>");
dataWriter.Close();

request.BeginGetResponse(SingleJsonCallBack, request);
}

private void SingleJsonCallBack(IAsyncResult result)
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
HttpWebResponse response =
(HttpWebResponse)request.EndGetResponse(result);
Stream responseStream = response.GetResponseStream();

UIThread.Post(UpdateUiText, responseStream);
}

Because REST services don’t have methods, you need to add any data to be sent to the service to the message being sent. In snippet 6.8, instead of calling BeginGetResponse from the initial call, you call BeginGetRequestStream . This event handler allows you to add information to the stream after it’s created but before it’s sent to the service. Once that has been done, you register the BeginGetResponse event handler as done during GET operations.

Knowing how to do GETs and POSTs is only half of the battle; you need to be able to use what gets returned as well. REST services normally return either XML- or JSON- formatted data. In section 6.3, we’re going to talk about ways to take the response stream containing these common data formats and convert it into useful objects.

6.3. Making the data usable

We’ve now discussed ways to request data from both SOAP and REST services. What we haven’t talked about is how to do anything with what you’ve received. In the case of a SOAP service using a service reference, you have strongly typed objects to deal with. In the case of a REST service, you typically receive raw XML or JSON. Luckily, Silverlight gives you several ways to take the incoming data and make it usable in your application. In the following sections, we’ll show you how to deserialize a stream containing either POX or JSON. In addition, we’ll talk about a specialized way to work with feeds following either the RSS or the Atom standard. Several examples in this section make use of a publicly available service hosted by http://www.geonames.org. This service returns geographic and demographic data in various formats including XML and JSON. Connecting to free services like this is a great way to test how to connect to remote systems.

6.3.1. Reading POX

Plain Old Xml (POX) has been the data format of choice on the internet for nearly a decade. The fact that it’s human-readable, customizable, and platform-independent virtually guaranteed its acceptance. Due to its long life and universal acceptance, the Silverlight team built in several ways to use POX after it’s available in the application. In this section, we’re going to describe the three major ways of using POX content. The three built-in methods to use XML content are LINQ to XML, also known as XLINQ, XmlReader, and XmlSerializer. In the following examples, we’ll demonstrate each of these ways of reading the same data using different methods.

Setting Up

For each example that follows, we’re using the same Silverlight application and the same web service call. First, we’ll show you what you’re getting and then how to deal with it.

Snippet 6.9. XAML Result: Getting the XML

<Grid x:Name="LayoutRoot" Background="#FF959595">
<Grid.RowDefinitions>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="5,5,0,0">
<TextBlock Text="Lat:"/>
<TextBox x:Name="txLat" Height="22" Width="85" Text="40.78343"/>
<TextBlock Text="Long:"/>
<TextBox x:Name="txLong" Height="22" Width="85" Text="-73.96625"/>
<Button x:Name="btnXML" Content="Get XML" Click="LoadXML"/>
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="5,5,0,0">
<TextBlock Text="City:"/>
<TextBlock x:Name="txCity" FontSize="12" FontFamily="Courier New"
VerticalAlignment="Center" />
</StackPanel>
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="5,5,0,0">
<TextBlock Text="Name:" />
<TextBlock x:Name="txName" FontSize="12" FontFamily="Courier New"
VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Grid.Row="3" Margin="5,5,0,0">
<TextBlock Text="Raw Results:" />
<TextBlock x:Name="txtResults" TextWrapping="Wrap"
FontFamily="Courier New" FontSize="11"/>
</StackPanel>
</Grid>

private void LoadXML(object sender, RoutedEventArgs e)
{
UIThread = SynchronizationContext.Current;

string uriPath =
"http://ws.geonames.org/neighbourhood?lat={0}&lng={1}&style=ful";
Uri uri = new Uri(string.Format(uriPath, txLat.Text, txLong.Text),
UriKind.Absolute);

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.BeginGetResponse(GetResults, request);
}

public void GetResults(IAsyncResult e)
{
HttpWebRequest request = (HttpWebRequest)e.AsyncState;
HttpWebResponse response =
(HttpWebResponse)request.EndGetResponse(e);
Stream responseStream = response.GetResponseStream();
UIThread.Post(UpdateUiText, responseStream);
}

In snippet 6.9, you’re using the HttpWebRequest object to retrieve the response stream from a web service that returns POX. This approach uses the same method described in section 6.2.2. The following examples show different versions of the UpdateUiText method, one for each way of parsing POX.

XLINQ

XLINQ is the newest way of parsing usable data from an XML stream. We discussed LINQ to XML in chapter 5. Everything discussed earlier will work in this context.

Snippet 6.10. C#: Using XLINQ
public void UpdateUiText(object stream)
{
XmlReader responseReader = XmlReader.Create((Stream)stream);
XElement xmlResponse = XElement.Load(responseReader);
XElement root = xmlResponse.Element("neighbourhood");

txtResults.Text = root.ToString();
txCity.Text = (string)root.Element("city");
txName.Text = (string)root.Element("name");
}

Snippet 6.10 shows the LINQ to XML version of UpdateUiText. It shows how using LINQ to access the data contained within individual XML elements is incredibly simple. The first step is to create an XmlReader from the response stream . You can then load that into an XElement . You can then access any element or attribute by name to get its value. This is a simple example of LINQ to XML, but it can be even more powerful when used to parse larger XML structures. Next, let’s look at using the XmlReader directly.

XmlReader

It’s entirely possible to use the XmlReader itself, without using a higher-level object to parse the XML for us.

Snippet 6.11. C#: Using XmlReader
public void UpdateUiText(object stream)
{
XmlReader responseReader = XmlReader.Create((Stream)stream);
responseReader.Read();

responseReader.ReadToFollowing("city");
string city = responseReader.ReadElementContentAsString();
responseReader.ReadToFollowing("name");
string name = responseReader.ReadElementContentAsString();
responseReader.ReadEndElement();
responseReader.ReadEndElement();

txCity.Text = city;
txName.Text = name;
}

Snippet 6.11 uses the plain XmlReader to step through the returned XML to find the values you want. This approach is rather clunky but does work. Because the XmlReader is forward-only, you have to be careful to get everything you need from the information the first time through. In snippet 6.12 you can see the same results using the XmlSerializer from the System.Xml.Serialization namespace, which is included in the SDK, so you need to add a reference to use it in your application.

XmlSerializer

The XmlSerializer provides a way to convert an XmlReader into strongly typed objects. This approach takes a little more setup but is incredibly useful in many business applications.

Snippet 6.12. C#: Using XmlSerializer
public void UpdateUiText(Object stream)
{
XmlReader responseReader = XmlReader.Create((Stream)stream);
responseReader.ReadToFollowing("neighbourhood");
XmlSerializer serializer = new XmlSerializer(typeof(Neighbourhood));
neighbourhood hood =
(Neighbourhood)serializer.Deserialize(responseReader);

txCity.Text = hood.city;
txName.Text = hood.name;
}
...
public class Neighbourhood
{
public string countryCode { get; set; }
public string countryName { get; set; }
public string adminCode1 { get; set; }
public string adminName1 { get; set; }
public string adminCode2 { get; set; }
public string adminName2 { get; set; }
public string city { get; set; }
public string name { get; set; }
}

You can see that using an XmlSerializer allows you to create a strongly typed object from the incoming XML data. To use this approach, you need to define a class that matches the format of the incoming XML. This class can be defined in your application itself, in a referenced class library, or in a service reference proxy. The first step is to move your XmlReader to the correct location . You also need to create a new XmlSerializer and initialize it with the type you want the XML deserialized into . The final step is to use the Deserialize method on the XmlSerializer instance you just created and pass in the XmlReader . The Deserialize method returns an object of the type it’s defined as or, if the deserialization failed, null.

Now that you’ve seen how to use POX, let’s look at another common data format. In the next section, you’ll learn how to use JSON-formatted data. JSON can be returned from a RESTful service just as easily as XML can, so let’s dig into that format now.

6.3.2. Converting JSON

If you’ve worked with AJAX, it’s possible that you’re already familiar with JSON. JSON provides a relatively simple way to create data objects using JavaScript. Because JSON is already prevalent in client-side programming, Microsoft has made a simple way to convert managed objects into, and out of, JSON objects. Snippet 6.13 shows a sample of what a JSON object looks like. You can accomplish this conversion in a couple ways such as using a DataContractJsonSerializer or even using LINQ syntax against a JsonObject. For snippet 6.13, let’s use the same method to load the data as was used in snippet 6.9 but change the URI to ws.geonames.org/neighbourhoodJSON?lat={0}&lng={1}.

Snippet 6.13. JSON Result: Sample JSON

{
"neighbourhood": {
"adminName2": "New York County",
"adminCode2": "061",
"adminCode1": "NY",
"countryName": "United States",
"name": "Central Park",
"countryCode": "US",
"city": "New York City-Manhattan",
"adminName1": "New York"
}
}

This snippet represents a simple but typical JSON object. As you can see, the returned JSON represents the same object as the XML returned in snippet 6.9. Luckily, the methods for converting the JSON object into a useful format are similar as well. Let’s start by taking a look at using the JSONObject syntax.

JsonObject

As with XML, there’s more than one way to use JSON-formatted data returned from a web service. The JSON being deserialized in these examples is shown in snippet 6.14. The ways of working with JSON data differ greatly as the following examples will show.

Snippet 6.14. C#: Reading JSON with the JsonObject
public void UpdateUiText(Object stream)
{
JsonObject hood = (JsonObject)JsonObject.Load((Stream)stream);
txCity.Text = hood["neighbourhood"]["city"];
txName.Text = hood["neighbourhood"]["name"];
txtResults.Text = hood.ToString();
}

One way to read JSON data is by using JsonObject.Load to convert the stream into an object based on the structure found within the stream itself. To get access to the JsonObject, you need to add a reference to the System.Json assembly and add a Using statement for the same namespace to the page. Once the JsonObject has been created, it’s a matter of using the name of the property you want as the key. If the property is nested, you add more keys as when accessing the city property inside the Neighbourhood class.

DataContractJsonSerializer

Another way to access returned JSON involves using the DataContractJsonSerializer to deserialize the stream into objects of predefined types. This new object to serialize and deserialize JSON is included in the System.Runtime.Serialization.Json namespace and in the System.ServiceModel.Web assembly. The two methods of the DataContractJsonSerializer are ReadObject and WriteObject, which deserialize and serialize JSON objects respectively. Snippet 6.15 uses the Neighbourhood class defined in snippet 6.12.

Snippet 6.15. C#: Reading JSON with the DataContractJsonSerializer
public class MyResults
{
public Neighbourhood neighbourhood { get; set; }
}
...
public void UpdateUiText(Object stream)
{
DataContractJsonSerializer ser =
new DataContractJsonSerializer(typeof(MyResults));
MyResults hood = (MyResults)ser.ReadObject((Stream)stream);

txCity.Text = hood.neighbourhood.city;
txName.Text = hood.neighbourhood.name;
}

This snippet shows the classes that hold the data once it’s deserialized, as well as the method that does the work. This approach is quite simple. First, you instantiate the DataContractJsonSerializer with the type of object you want filled. All that’s left is to pass the response stream into the ReadObject method of the DataContractJsonSerializer you just created. You access the data as you would with any other strongly typed .NET object. In the case of two well known schemas, RSS and Atom, there’s no need to deserialize the stream yourself. We’re going to take a look at these specialized classes, which make consuming published feeds easy and straightforward.

6.3.3. Reading syndicated feeds

Atom and RSS are XML formats that make it easy to share information. This information can be broadcast over the internet and is generally referred to as a feed. Each feed can be subscribed to through an application that has the ability to render the feed information. Silverlight is one such platform that can be used to work with Atom and RSS feeds through the System.ServiceModel.Syndication namespace and the assembly of the same name. Note that this assembly is part of the SDK and not the native Silverlight installation, so you need to add a reference to it in your application.

The System.ServiceModel.Syndication namespace contains rich support for the Atom 1.0 and RSS 2.0 feed formats. These feed formats can be read and written through an instance of the SyndicationFeedFormatter class. This class abstracts the details of a feed format so you can focus on the contents of a specific feed. The contents of a feed are stored in a SyndicationFeed object, which enables you to read and display syndicated feed information.

Reading Syndicated Feeds

Reading syndicated content is an incredibly easy process. This process is simple because both Atom and RSS feeds are represented as XML, and you can use the XmlReader class’s Create method to quickly read the XML. Then, you can take this XML and load it into a SyndicationFeed. This is important because, as you’ll see in a minute, a SyndicationFeed gives you all sorts of goodies. But first, take a look at how to load a syndicated feed (snippet 6.16).

Snippet 6.16. C#: Downloading and displaying an RSS feed
private void btnRSS_Click(object sender, RoutedEventArgs e)
{
uiThread = SynchronizationContext.Current;
uri = new Uri("http://feeds.feedburner.com/ToCodeOrNotToCode");

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.BeginGetResponse(ReadRSS, request);
}

public void ReadRSS(IAsyncResult e)
{
HttpWebRequest request = (HttpWebRequest)e.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(e);
Stream responseStream = response.GetResponseStream();

XmlReader reader = XmlReader.Create(responseStream);
SyndicationFeed feed = SyndicationFeed.Load(reader);

response.Close();
uiThread.Post(ShowRSS, feed);
}

The code in snippet 6.16 shows how to load an Atom 1.0 or RSS 2.0 feed over the http:// protocol. Each feed format exposes some really valuable and useful information. Unfortunately, this information is defined differently across each feed format. Thankfully, the SyndicationFeed class abstracts those details for you, empowering you to quickly get to this information through a common interface. This interface contains a variety of useful properties associated with the overall feed. These properties are listed and described in table 6.2.

Table 6.2. The main feed properties available through a SyndicationFeed. Each property is shown with the element or property it corresponds with in the Atom 1.0 and RSS 2.0 specifications.

Property

Atom 1.0

RSS 2.0

Description

Authors Author managingEditor/a10:author A collection of SyndicationPerson objects. Each item in the collection represents an individual who’s considered to be an author of the feed.Atom feeds are guaranteed to have at least one author.
Categories Category Category A collection of SyndicationCategory objects. Each item in the collection is a category that the feed can belong to. Categories are used by aggregators to logically group similar feeds. An RSS orAtom feed can belong to multiple categories.Feeds are not required to belong to any categories.
Contributors contributor a10:contributor A collection of SyndicationPerson objects. Each object in the collection represents someone who has contributed to creating an Atom feed. A contributor isn’t as central to the production of a feed as an author.
Copyright Rights Copyright Represents the copyright information related to the feed.
Description Subtitle Description Provides a small summary of the feed.
Generator generator Generator The name of the application used to create the feed.
Id id a10:id The unique identifier of an Atom feed represented as a string. This value will be null for RSS feeds. This value may be anything from a URL to a GUID.
ImageUrl Logo Image The URL of the image associated with the feed. This image generally represents the branding associated with a feed.
Language Lang language This optional value represents the language that the feed was written in.
LastUpdatedTime updated lastBuildDate The time at which the feed was last updated.
Links link a10:link A collection of SyndicationLink elements. Each SyndicationLink represents a link available within the feed.
Title title Title The general title of the feed.

This table shows the vast amount of information available through each feed. This information is valuable; but, the most valuable information is stored in a property called Items. This property gives you access to the collection of feed items available within a SyndicationFeed. Each item in this collection is a SyndicationItem, which represents an individual syndicated feed item.

Understanding the Feed Items

The Items property of a SyndicationFeed object represents the collection of items in a feed. Each element in the collection is defined as a SyndicationItem. This class consolidates all the properties and features of an RSS 2.0 item or Atom 1.0 entry. All the available properties and how they relate to both RSS and Atom are listed in table 6.3.

Table 6.3. The properties available for a SyndicationItem

Property

Atom 1.0

RSS 2.0

Description

Authors author managingEditor or a10:author A collection of SyndicationPerson objects. Each item in the collection represents an individual who’s considered to be an author of the item. Atom feeds are guaranteed to have at least one author.
Categories category category A collection of SyndicationCategory objects. Each item in the collection is a category that the item belongs to. An item can belong to multiple categories.But, items aren’t required to belong to any categories.
Content content a10:content The content of the item.
Contributors contributor a10:contributor A collection of SyndicationPerson objects. Each object in the collection represents someone who has contributed to creating an Atom feed. A contributor isn’t as central to the production of a feed as an author.
Copyright rights Copyright Any copyright information for the item.
Id id a10:id Unique ID for the item provided by the feed.
LastUpdatedTime updated a10:updated A DateTimeOffset indicating the last modified time for the item.
Links link Link A collection of SyndicationLink elements. Each SyndicationLink represents a link available within the feed.
PublishDate published pubDate A DateTimeOffset indicating published time for the item.
SourceFeed source Source The SyndicationFeed of which the item is a member.
Summary summary Description Summary of the item.
Title title Title The title of the syndicated item.

That’s enough talk about the properties and what they do; now let’s put some of them to use.

Using SyndicatedFeed with RSS

The RSS format allows content publishers to syndicate pure information, based on the fact that RSS supports only plain text. Although other formats, such as HTML, can be used, these formats must be escaped to be a keep the feed valid. By keeping content in a pure text format, consuming applications can completely determine the look and behavior of the information. Snippet 6.17 demonstrates the output of the RSS feed.

Snippet 6.17. XML: An example RSS feed based on some blog
<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0">
<channel>
<title>Chad Campbell's Blog</title>
<link>http://somedomain.com/</link>
<description>My blog</description>

<item>
<title>My Latest Blog Post!</title>
<link>http://somedomain.com/myLatestBlogPost.html</link>
<description>Content of the latest post</description>
</item>
<item>
<title>My First Blog Post!</title>
<link>http://somedomain.com/myFirstBlogPost.html</link>
<description>Content of my first post</description>
</item>
</channel>
</rss>

The content in this snippet doesn’t come from a real RSS feed, but it does illustrate the basic format of an RSS feed. As you can see, the feed is represented as basic XML. Each element within the XML contains basic textual content.

Here’s a simple request of an RSS feed that then displays the titles of the returned SyndicationItems in a ListBox. You’re taking the stream that you get from the call and reading that into an XmlReader. Then it’s a simple matter to load that XmlReader directly into a SyndicationFeed. You can then bind the Items collection of the SyndicationFeed object directly to the ListBox to show the feeds. In snippet 6.18, you complete the call started in snippet 6.16.

Snippet 6.18. C# XAML Result: Displaying Syndicated content

<Button Height="21" HorizontalAlignment="Left" VerticalAlignment="Bottom"
Width="68" Content="Load RSS" Margin="8,0,0,8" x:Name="btnRSS"
Click="btnRSS_Click"/>
<ListBox Height="256" Margin="0,8,8,0" x:Name="FeedList"
VerticalAlignment="Top" ItemsSource="{Binding Mode=OneWay}"
HorizontalAlignment="Right" Width="384">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title.Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

public void ShowRSS(Object rawFeed)
{
FeedList.ItemsSource = (rawFeed as SyndicationFeed).Items;
}

You can see how easy it is to display RSS content in your Silverlight application, but there can be a problem with this approach. It’s becoming common practice to include HTML content in the RSS feed. Although this looks nice in a feed aggregator, Silverlight can’t display HTML content. For this reason, you may want to consider using Atom feeds whenever possible.

Reading Atom

Atom is a newer entry into the world of online syndication. This format gives content providers the flexibility to specify the type of content they’re serving—you can deliver XML markup within the feed itself. This characteristic is significant for Silverlight developers because it allows for the publishing of content formatted via XAML. Consider the Atom feed in snippet 6.19.

Snippet 6.19. XML: An example Atom feed based on some blog
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Chad Campbell's Blog</title>
<link href="http://somedomain.com/" rel="self"/>
<updated>2007-11-04T11:27:52Z</updated>
<author><name>Chad Campbell</name></author>
<id>http://somedomain.com/</id>

<entry>
<title>My Latest Blog Post!</title>
<link href="http://somedomain.com/myLatestBlogPost.html"/>
<id>http://somedomain.com/myLatestBlogPost.html</id>
<updated>2007-10-22T12:12:43Z</updated>
<content type="text/xml"><TextBlock Foreground="Red">
Formatted content of the latest post
</TextBlock></content>
</entry>

<entry>
<title>My First Blog Post!</title>
<link href="http://somedomain.com/myFirstBlogPost.html"/>
<id>http://somedomain.com/myFirstBlogPost.html</id>
<updated>2007-10-15T10:32:47Z</updated>
<content type="text/xml"><TextBlock Foreground="Red">
Formatted content of my first post
</TextBlock></content>
</entry>
</feed>

Because the Content of the entry is formatted as XML and is valid XAML, it’s possible to create Silverlight controls on the fly based on the contents of the Atom feed. Why is this useful because the biggest users of Atom currently are blogs and news services? Because this provides a nice way to get dynamically created controls into Silverlight from a feed of your own making. Now you’ve seen how to get data in various formats using various connection types. The methods we’ve already shown for accessing data are probably enough for most applications; in the next section, we’ll take it even further and show you how to do advanced error handling with SOAP services, as well as two different ways to push data to your Silverlight application.

6.4. Using advanced services

You’ve seen how to download data and various ways to parse the returned data streams into usable pieces. Let’s now talk about a few special networking cases. Some SOAP services can be crafted in such a way as to provide additional functionality beyond basic SOAP. Windows Communication Foundation (WCF) is part of the .NET Framework 3.0 and provides a framework for creating SOAP web services. Although this technology is fairly new, it’s growing in usage.

Another special case is that of two-way services, also known as push service. Silverlight supports two kinds of push technology in the form of WCF duplex services and TCP sockets. Although the topics in this section are more complex, the abilities to add push communications and advanced error handling on service calls make this all good knowledge.

6.4.1. WCF service enhancements

Connecting to a WCF service is accomplished in the same way as connecting to any other SOAP service, as described in section 6.2.1. Creating a service reference allows the use of a client proxy, which exposes all referenced types and methods to the Silverlight application. WCF can expose features not allowed in Silverlight; so, when creating a WCF service for Silverlight consumption, there are a few restrictions. We’ve already stated that Silverlight supports only the 1.1 version of the SOAP protocol. Another limitation is that Silverlight doesn’t support the WS-* series of protocols made available through WCF.

Due to Silverlight’s service limitations, Visual Studio has a special template for creating a WCF service to be consumed by Silverlight called Silverlight-Enabled WCF Service. Describing how to create a WCF service is beyond the scope of this book, but the template should help ensure that the service is consumable by Silverlight. If you create your own WCF service, you have the ability to enhance the error handling capability of calls to it from Silverlight.

Error Handling

One of the nice things about WCF is the ability to throw exceptions on a service call. Unfortunately, Silverlight doesn’t support this. Any exception thrown by the service gets translated by the browser into a 404 File Not Found error. The creator of the WCF service can still add error messages by adding them as an OUT parameter.

When the signature of the WCF service contains an OUT parameter, you can access it directly through the EventArgs on the event handler for the completed call, as shown in snippet 6.20.

Snippet 6.20. SL WCF: Reading an OUT parameter
[OperationContract]
string GetSomeData(int Id, out MyErrorObject myError);

void serviceProxy_GetSomeDataCompleted(object sender,
GetSomeDataCompletedEventArgs e)
{
if (e.Error != null)
{
tbxMessage.Text = e.Error.Message;
}
if (e.myError != null)
{
tbxMessage.Text = e.myError.Message;
}
else
{
tbxMessage.Text = e.Result.ToString();
}
}

In this snippet, you see a standard [ServiceMethod]Completed method like those shown throughout this chapter. This example also demonstrates error trapping and a custom out parameter. Now that you’ve seen standard WCF services, let’s dig deeper and look at how WCF duplex services can enable you to push data from a server to Silverlight.

6.4.2. WCF duplex services

So far we’ve talked about ways to send and receive data that require the Silverlight application to initiate each individual request. A couple of options allow the server to push data directly to your application. Duplex WCF services and sockets each provide a channel that allows properly configured server resources to send data without a request each time.

Duplex services give the server the ability to send, or push, data directly to the client as it becomes available. AJAX applications have to send a request for updates to the server that execute on a loop with a timer. This approach, known as polling, creates overhead, both in your application and on the network, that can be avoided by using these techniques.

Duplex communication is possible in Silverlight using properly configured WCF services. This is quite useful if you’re building an application that needs to receive notifications of changed properties on the server, such as when scores change in a sporting event. To enable duplex communication within Silverlight, a pair of new assemblies, both named System.ServiceModel.PollingDuplex.dll, need to be referenced, one by the application hosting the duplex service and the other by your Silverlight application. They’re identifiable by their location within the Silverlight SDK because one is in the Libraries\Server path, and the other in the Libraries\Client path.

Connecting to the Service

Once you have a functioning WCF service set up to enable duplex communication, let’s attach it to a Silverlight application. In the test application, you have a Button that initiates the duplex communication and a TextBlock that displays the results so that the XAML is quite simple. Here, you’re building a simple application that registers with the service to get updates on scores from a game (snippet 6.21).

Snippet 6.21. XML Result: Sample application to get score updates

<Grid x:Name="LayoutRoot" Background="#FF6F93C3" Width="300">
<Button Height="41" HorizontalAlignment="Stretch"
VerticalAlignment="Top" Content="Get Scores!"
Margin="199,8,0,0" x:Name="btnGetScores"
Click="btnGetScores_Click"/>
<TextBlock Height="41" Margin="8,8,0,0" VerticalAlignment="Top"
Text="Get scores for your team!" TextWrapping="Wrap" Width="187"
HorizontalAlignment="Left" FontFamily="Arial" FontSize="20"/>
<TextBlock Margin="8,70,8,8" Text="" TextWrapping="Wrap"
x:Name="txtScores" FontFamily="Courier New" FontSize="10"/>
</Grid>

The only thing of note in the XAML is that the Click attribute of the button points to the btnGetScores_Click method, which we’ll discuss following snippet 6.22. In this snippet, you grab a link to the current SynchronizationContext before beginning your asynchronous operations. This ensures that you always have a way to update the user interface.

Snippet 6.22. C#: Starting the process
SynchronizationContext uiThread;

private void btnGetScores_Click(object sender, RoutedEventArgs e)
{
uiThread = SynchronizationContext.Current;

PollingDuplexHttpBinding poll = new PollingDuplexHttpBinding();
poll.InactivityTimeout = TimeSpan.FromMinutes(1);

IChannelFactory<IDuplexSessionChannel> channelFactory =
poll.BuildChannelFactory<IDuplexSessionChannel>(new
BindingParameterCollection());

IAsyncResult factoryOpenResult =
channelFactory.BeginOpen(new
AsyncCallback(OnOpenFactoryComplete), channelFactory);

if (factoryOpenResult.CompletedSynchronously)
{
OpenTheChannel(factoryOpenResult);
}
}

In snippet 6.22, you start the process of binding to a duplex web service. You begin by creating a PollingDuplexHttpBinding object, on which you set the timeout properties. You then use that polling object to create an IChannelFactory of type IDuplexSessionChannel. The next step is to begin the asynchronous call using the BeginOpen method of the factory you just created.

One thing you’ll see throughout these samples is the calling of CompletedSynchronously immediately after the asynchronous call. You do this in case the response is immediate, as can be the case for some small asynchronous operations. With this in mind, note that all the asynchronous calls are to methods that also check the CompletedSynchronously property and then either return or call the proper next method. Snippet 6.23 is an example of one such method.

Snippet 6.23. C#: Example asynchronous handler
void OnOpenFactoryComplete(IAsyncResult result)
{
if (result.CompletedSynchronously)
return;
else
OpenTheChannel(result);
}

For the rest of this sample, we won’t show the On[action]Completed methods because they all follow the pattern of this snippet. The next step is to open the channel with the WCF service and to begin polling it for queued messages (snippet 6.24).

Snippet 6.24. C#: Opening duplex channel and polling it
void OpenTheChannel(IAsyncResult result)
{
IChannelFactory<IDuplexSessionChannel> channelFactory =
(IChannelFactory<IDuplexSessionChannel>)result.AsyncState;

channelFactory.EndOpen(result);

IDuplexSessionChannel channel = channelFactory.CreateChannel(new
EndpointAddress("http://localhost:51236/ScoreService.svc"));

IAsyncResult channelOpenResult = channel.BeginOpen(new
AsyncCallback(OnOpenChannelComplete), channel);

if (channelOpenResult.CompletedSynchronously)
{
StartPolling(channelOpenResult);
}
}
void StartPolling(IAsyncResult result)
{
IDuplexSessionChannel channel =
(IDuplexSessionChannel)result.AsyncState;

channel.EndOpen(result);
Message message =
Message.CreateMessage(channel.GetProperty<MessageVersion>(),
"Silverlight/IScoreService/Register","Baseball");

IAsyncResult resultChannel = channel.BeginSend(message,
new AsyncCallback(OnSendComplete), channel);

if (resultChannel.CompletedSynchronously)
{
CompleteOnSend(resultChannel);
}

PollingLoop(channel);
}

The method OpenTheChannel in this snippet shows where you define the service you’re attempting to connect to. It’s assigned as an endpoint on the duplex channel. The StartPolling method creates the SOAP message for the initial call and sends it to the service.

Snippet 6.25 shows that CompleteOnSend receives the response from the initial call. This is also the first use of the uiThread SynchronizationContext to update text in the XAML.

Snippet 6.25. C#: Looking for messages
void CompleteOnSend(IAsyncResult result)
{
IDuplexSessionChannel channel =
(IDuplexSessionChannel)result.AsyncState;

channel.EndSend(result);
uiThread.Post(UpdateScore, "Registered!" + Environment.NewLine);
}

void UpdateScore(object text)
{
txtScores.Text += (string)text;
}

void PollingLoop(IDuplexSessionChannel channel)
{
IAsyncResult result =
channel.BeginReceive(new AsyncCallback(OnReceiveComplete),
channel);
if (result.CompletedSynchronously)
CompleteReceive(result);
}

The PollingLoop method assigns CompleteReceive (snippet 6.26) as the method to handle messages received from the duplex service and then closes the channel once the game is over.

Snippet 6.26. C#: Reading message
void CompleteReceive(IAsyncResult result)
{
IDuplexSessionChannel channel =
(IDuplexSessionChannel)result.AsyncState;

try
{
Message receivedMessage = channel.EndReceive(result);

if (receivedMessage == null)
{
uiThread.Post(UpdateScore, "Channel Closed");
}
else
{
string text = receivedMessage.GetBody<string>();
uiThread.Post(UpdateScore, "Score Received: " +
text + Environment.NewLine);

if (text == "Game Over")
{
IAsyncResult resultFactory =
channel.BeginClose(new AsyncCallback(OnCloseChannelComplete),
channel);
if (resultFactory.CompletedSynchronously)
{
CompleteCloseChannel(result);
}
}
else
{
PollingLoop(channel);
}
}
}
catch (CommunicationObjectFaultedException)
{
uiThread.Post(UpdateScore, "Channel Timed Out");
}
}

void CompleteCloseChannel(IAsyncResult result)
{
IDuplexSessionChannel channel =
(IDuplexSessionChannel)result.AsyncState;

channel.EndClose(result);
}

Creating and consuming a duplex-enabled WCF service take more effort than a standard SOAP service, but there definitely are benefits. The ability to open a channel to the server to get requests as they are available is quite powerful. Another approach you can take to accomplish this uses sockets, which we’ll discuss next.

6.4.3. Connecting to sockets

We’ve already discussed using a specially configured WCF service to enable push communications, so now let’s talk about using sockets for the same purpose. A socket is a communications endpoint that enables a connection to be established. Once it’s established, information can flow in either direction along the open channel. The only socket protocol supported by Silverlight 2 is TCP, and the ports are restricted to the range of 4502–4534.

Sockets require a clientaccesspolicy.xml file with a few changes. The resource element isn’t used and is replaced with the socket-resource element. Both element types may exist in the file and apply the style to the specific type or request. Snippet 6.27 is an example of a simple client access policy giving access to sockets using TCP over port 4502.

Snippet 6.27. XML: clientaccesspolicy.xml for socket access
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from>
<domain uri="*"/>
</allow-from>
<grant-to>
<socket-resource port="4502" protocol="tcp"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>

Opening a socket connection with a socket server can be done in a few simple steps that are quite similar to the other forms of communicating you’ve already seen. The first step is to open the socket.

Snippet 6.28. C#: Snippet 6.28 Opening TCP socket
public void OpenTheSocket()
{
DnsEndPoint tcpEndpoint =
new DnsEndPoint(Application.Current.Host.Source.DnsSafeHost, 4502);
Socket tcpSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);

SocketAsyncEventArgs socketArgs = new SocketAsyncEventArgs();
socketArgs.UserToken = tcpSocket;
socketArgs.RemoteEndPoint = tcpEndpoint;

socketArgs.Completed +=
new EventHandler<SocketAsyncEventArgs>(socketArgs_Completed);
tcpSocket.ConnectAsync(socketArgs);
}

Snippet 6.28 shows the creations of an endpoint and a socket and using these to asynchronously request a connection to the remote socket server. You use Application.Current.Host.Source.DnsSafeHost to get the IP address of the host of the Silverlight application in a form usable in creating a socket endpoint. Using this technique for creating the endpoint is only useful when the socket and the Silverlight application are hosted in the same location. Once you’ve requested the connection, you need to handle the response.

Snippet 6.29. C#: Handling response
public void socketArgs_Completed(object sender,
SocketAsyncEventArgs receivedArgs)
{
switch (receivedArgs.LastOperation)
{
case SocketAsyncOperation.Connect:
if (receivedArgs.SocketError == SocketError.Success)
{
byte[] response = new byte[1024];
receivedArgs.SetBuffer(response, 0, response.Length);
Socket socket = (Socket)receivedArgs.UserToken;
socket.ReceiveAsync(receivedArgs);

}
else
throw new SocketException((int)receivedArgs.SocketError);
break;
case SocketAsyncOperation.Receive:
ReceiveMessageOverSocket(receivedArgs);
break;
}
}

You can determine the type of response by evaluating the value of the LastOperation property of SocketAsyncEventArgs, as shown in table 6.4.

Table 6.4. Values of SocketAsyncOperation enumeration

Value

Description

None Connection not yet established
Connect Connection established
Receive Packets received
Send Packets sent

Now you need to set up the connection to receive data, as shown in snippet 6.30.

Snippet 6.30. C#: Receiving message
public void ReceiveMessageOverSocket(SocketAsyncEventArgs receivedArgs)
{
string message = Encoding.UTF8.GetString(receivedArgs.Buffer,
receivedArgs.Offset, receivedArgs.BytesTransferred);

UIThread.Post(UpdateUIControls, message);

Socket socket = (Socket)receivedArgs.UserToken;
socket.ReceiveAsync(receivedArgs);
}

When the message comes in, it needs to be converted into the correct format, a string in this case; it can then be deserialized using any of the methods described in previous sections depending on the format of the incoming data.

6.5. Summary

We know that at times this chapter has been complex and that some aspects of it may not seem intuitive to everyone. Networking and communications are often-complicated, but always-needed, pieces of any platform; Silverlight is no different in that respect. Most applications of any size will need to access data at some point. Silverlight provides numerous ways to connect to, download, and use a variety of types of data. With support for technologies ranging from the decade old POX to the not-yet-released ADO.NET Data Services, there’s sure to be something to fit into any application framework.

Data might add the meat to your application, but what’s a meal without dessert? Luckily, Silverlight has support for both audio and video that can be incredibly impressive and can provide the crème brülée to any good Silverlight meal. In the next chapter, we’ll discuss how to add these impressive elements to your application, allowing you to create truly rich applications for your clients and customers.