Chapter 7. Data Access and Networking

Data access in Silverlight applications works differently than it does in traditional applications. You'll need to be aware of how it works and the limitations. In this chapter, you will look at what makes data access different, and then explore mechanisms for accessing data in a Silverlight application.

Data Access in Silverlight Applications

As discussed in Chapter 1, RIAs bridge the gap between Windows-based smart clients and web-based applications. When moving to this type of environment, data access and networking can be confusing.

In a Windows-based smart client, the application has access to the database at all times. It can create a connection to the database, maintain state with the database, and remain connected.

On the other hand, a web application is what is known as a pseudo-conversational environment, which is, for the most part, a completely stateless and disconnected environment. When a client makes a request to the web server, the web server processes the request and returns a response to the client. After that response has been sent, the connection between the client and the server is disconnected, and the server moves on to the next client request. No connection or state is maintained between the two.

In Silverlight applications, we have one additional layer of complexity. The application runs from the client's machine. However, it is still a disconnected environment, because it is hosted within a web browser. There is no concept of posting back for each request or creating a round-trip to the server for data processing. Therefore, data access is limited to a small number of options.

In addition, a Silverlight application has a number of security restrictions placed on it to protect the users from the application gaining too much control over their machine. For instance, the Silverlight application has access to only an isolated storage space to store its disconnected data. It has no access whatsoever to the client's hard disk outside its "sandbox." Silverlight's isolated storage is discussed in more detail in Chapter 9.

What are your options for accessing data in a Silverlight application? The following main mechanisms are available:

  • The most common mechanism to access data from a Silverlight application is through web services, typically a WCF service.

  • Silverlight applications can access data using ADO.NET Data Services, which provides access to data through a URI syntax.

  • Silverlight also has built-in socket support, which allows applications to connect directly to a server through TCP sockets.

  • Silverlight has out-of-the-box support for JavaScript Object Notation (JSON), as well as RSS 2.0 and Atom 1.0 syndication feed formats.

Of these mechanisms, I'll explore accessing WCF services from Silverlight in depth, and then have a high-level look at using sockets. For examples and more information on accessing other data services, refer to Pro Silverlight 4 in C# by Matthew MacDonald (Apress, 2010).

Accessing Data Through Web Services

One of the ways that a Silverlight application can access data is through web services. These can be ASP.NET Web Services (ASMX), Windows Communication Foundation (WCF) services, or representational state transfer (REST) services. Here, you will concentrate on using a WCF service, which is the preferred way of accessing data in a Silverlight application through web services.

Try It Out: Accessing Data Through a WCF Service

To demonstrate accessing data from a WCF service, you will build the same application that you built in Chapter 5 to try out the DataGrid. (For more information about any part of this exercise regarding the DataGrid, refer back to Chapter 5.) The difference will be that the application will get the data through a web service.

As you'll recall, this application displays common starting hands in poker and the nicknames that have been given to those starting hands. The UI will have three columns: the first column will display two images of the cards in the hand, the second column will display the nickname, and the third column will contain notes about the hand. The completed application is shown in Figure 7-1.

The poker starting hands application

Figure 7.1. The poker starting hands application

  1. Create a new Silverlight application in Visual Studio 2010. Call the application WCFService, and allow Visual Studio to create a Web Application project named WCFService.Web to host your application.

    Right-click the WCFService.Web project and select Add

    The poker starting hands application
    Adding the StartingHands.cs class to the project

    Figure 7.2. Adding the StartingHands.cs class to the project

  2. Now you need to implement the StartingHands.cs class. It is very similar to the class used in Chapter 5's DataGrid example. To save yourself some typing, you can copy the code from that project. As shown in bold in the following code, the only differences are the namespace and the return type of the GetHands() method. Instead of using an ObservableCollection, it will return a simple List<StartingHands>.

    Note

    In a real-world example, the StartingHands.cs class would be doing something like retrieving data from a SQL Server database and executing some business logic rules on the data. For simplicity, this example just returns a static collection.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace WCFService.Web
    {
        public class StartingHands
        {
            public string Nickname { get; set; }
            public string Notes { get; set; }
            public string Card1 { get; set; }
            public string Card2 { get; set; }
    
            public static List<StartingHands> GetHands()
            {
                List<StartingHands> hands = new List<StartingHands>();
    
                hands.Add(
                    new StartingHands()
                    {
                        Nickname = "Big Slick",
                        Notes = "Also referred to as Anna Kournikova.",
                        Card1 = "As",
                        Card2 = "Ks"
                    });
    
                hands.Add(
                new StartingHands()
                {
                    Nickname = "Pocket Rockets",
                    Notes = "Also referred to as Bullets.",
                    Card1 = "As",
                    Card2 = "Ad"
                });
    hands.Add(
                    new StartingHands()
                    {
                        Nickname = "Blackjack",
                        Notes = "The casino game blackjack.",
                        Card1 = "As",
                        Card2 = "Js"
                    });
    
                hands.Add(
                    new StartingHands()
                    {
                        Nickname = "Cowboys",
                        Notes = "Also referred to as King Kong",
                        Card1 = "Ks",
                        Card2 = "Kd"
                    });
    
                hands.Add(
                    new StartingHands()
                    {
                        Nickname = "Doyle Brunson",
                        Notes = "Named after poker great Doyle Brunson",
                        Card1 = "Ts",
                        Card2 = "2s"
                    });
    
    
                return hands;
            }
        }
    }
  3. Next, you need to add the WCF service that will call the StartingHands.GetHands() method. Right-click the WCFService.Web project and select Add

    Adding the StartingHands.cs class to the project
    Adding the Silverlight-enabled WCF service

    Figure 7.3. Adding the Silverlight-enabled WCF service

  4. This will add a service named StartingHandService.svc to the project with an attached code-behind file named StartingHandService.svc.cs. View that code behind. You will see that Visual Studio has already created the base WCF service, including a sample method called DoWork(), as follows:

    namespace WCFService.Web
    {
        [ServiceContract(Namespace = "")]
        [AspNetCompatibilityRequirements(
            RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
        public class StartingHandService
        {
            [OperationContract]
            public void DoWork()
            {
                // Add your operation implementation here
                return;
            }
    
            // Add more operations here and mark them with [OperationContract]
        }
    }
  5. Replace the DoWork() method with a GetHands() method that returns a List<StartingHands> collection, as follows:

    namespace WCFService.Web
    {
        [ServiceContract(Namespace = "")]
        [AspNetCompatibilityRequirements(RequirementsMode =
            AspNetCompatibilityRequirementsMode.Allowed)]
        public class StartingHandService
        {
            [OperationContract]
            public List<StartingHands> GetHands()        {
                    return StartingHands.GetHands();
            }
            // Add more operations here and mark them
            // with [OperationContract]
        }
    }

    This method simply returns the results from calling the StartingHands.GetHands() method. Note that you will need to add a using statement for System.Collections.Generic.

    Now that you have a Silverlight-enabled WCF service, you need to add a reference in your Silverlight project so that your Silverlight application can access the service. To do this, right-click References within the WCFService in Solution Explorer and select Add Service Reference, as shown in Figure 7-4. This brings up the Add Service Reference dialog box.

    Choosing to add a service reference

    Figure 7.4. Choosing to add a service reference

  6. In the Add Service Reference dialog box, click the Discover button, as shown in Figure 7-5.

  7. Visual Studio will find the StartingHandService.svc and will populate the Services list in the Add Service Reference dialog box. Note that you may need to build the solution before Visual Studio will find your service. Expand the StartingHandService.svc node to show the StartingHandService. Click StartingHandService to see the GetHands() web method in the Operations listing, as shown in Figure 7-6. Enter StartingHandServiceReference as the Namespace field, and then click OK to continue.

    Finding the services in the solution

    Figure 7.5. Finding the services in the solution

    Adding a service reference for StartingHandService

    Figure 7.6. Adding a service reference for StartingHandService

    Open the Visual Studio Object Browser by selecting View

    Adding a service reference for StartingHandService
    Object Browser for StartingHandService

    Figure 7.7. Object Browser for StartingHandService

  8. Look at the members listed on the right side of the Object Browser. There are a number of items that are added, but take specific note of the method named GetHandsAsync() and the event named GetHandsCompleted. You will need to use bothof these in order to call your web service from Silverlight.

  9. Now it's time to create the Silverlight application's UI. Open the MainPage.xaml file in Visual Studio. Place the cursor within the root Grid and double-click the DataGrid control in the Toolbox. Once the DataGrid has been added, right-click on it in designer and select Reset Layout

    Object Browser for StartingHandService
    <Grid x:Name="LayoutRoot" Background="White">
        <sdk:DataGrid AutoGenerateColumns="False" Name="dataGrid1" />
    </Grid>
  10. Rename the DataGrid to grdData and set the Margin to 15. Next, add the following Column definitions, which are from the previous DataGrid exercise in Chapter 5. The DataGrid contains three columns: one template column containing the two cards in the hand and two text columns containing the nickname and notes about the hand.

    <sdk:DataGrid AutoGenerateColumns="False" Name="grdData" Margin="15">
        <sdk:DataGrid.Columns>
            <sdk:DataGridTemplateColumn Header="Hand">
                <sdk:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition />
                            </Grid.ColumnDefinitions>
    
                            <Border
                                Margin="2" CornerRadius="4"
                                BorderBrush="Black"
                                BorderThickness="1" />
                            <Rectangle
                                Margin="4" Fill="White"
                                Grid.Column="0" />
                            <Border
                                Margin="2" CornerRadius="4"
                                BorderBrush="Black"
                                BorderThickness="1"
                                Grid.Column="1" />
                            <Rectangle
                                Margin="4" Fill="White"
                                Grid.Column="1" />
                            <TextBlock
                                Text="{Binding Card1}"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center" "
                                Grid.Column="0" />
                            <TextBlock
                                Text="{Binding Card2}"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center"
                                Grid.Column="1" />
    
                        </Grid>
                    </DataTemplate>
                </sdk:DataGridTemplateColumn.CellTemplate>
            </sdk:DataGridTemplateColumn>
    
            <sdk:DataGridTextColumn
                Header="Nickname"
                Binding="{Binding Nickname}"  />
            <sdk:DataGridTextColumn
                Header="Notes"
                Binding="{Binding Notes}" />
    
        </sdk:DataGrid.Columns>
    </sdk:DataGrid>
  11. Save the MainPage.xaml file and navigate to the code behind for the application, located in the MainPage.xaml.cs file. Wire up the Loaded event handler for the page, as follows:

    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(Page_Loaded);
        }
        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            throw new NotImplementedException();
        }
    }

    Next, you need to call the WCF service. In Silverlight, web services can be called only asynchronously, so the browser's execution is not blocked by the transaction. In order to do this, you need to get an instance of the service reference (commonly referred to as the web service proxy class) named StartingHandService, which you added earlier. You will then wire up an event handler for the service's GetHandsCompleted event, which you examined in the Object Browser (in step 11). This is the event handler that will be called when the service has completed execution. Finally, you will execute the GetHandsAsync() method.

    Tip

    In a real-world scenario, you will want to present the user with a progress bar or animation while the service is being called, since the duration of a web service call can be lengthy.

  12. Within the Page_Loaded event handler, first obtain an instance of StartingHandService. Then, in the GetHandsCompleted event handler, bind the ItemsSource of the DataGrid to the result returned from the service call, as shown in the following code. Note that normally you will want to check the result to make certain that the web service call was successful, and alert the user accordingly in case of failure.

    using WCFService.StartingHandServiceReference;
    
    ...
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(Page_Loaded);
        }
    
        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            StartingHandServiceClient service = new StartingHandServiceClient();
            service.GetHandsCompleted += new
                        EventHandler<GetHandsCompletedEventArgs>(
                        service_GetHandsCompleted);
            service.GetHandsAsync();
        }
    
        void service_GetHandsCompleted(object sender, GetHandsCompletedEventArgs e)
        {
            this.grdData.ItemsSource = e.Result;
        }
    }
  13. Test your application. If all goes well, you should see the populated DataGrid, as shown earlier in Figure 7-1.

This example demonstrated how to use the Silverlight-enabled WCF service provided in Visual Studio to allow your Silverlight application to access data remotely. As noted earlier in chapter in the section "Data Access in Silverlight Applications", this is one of the most common approaches to data access with Silverlight.

Accessing Services from Other Domains

In the previous example, the web service was on the same domain as your Silverlight application. What if you want to call a service that is on a different domain?

In fact, as a best practice, it is preferred to have your web services stored on a domain separate from your web application. Even for applications where you control both the web service and the Silverlight application, you may be dealing with different domains.

If you attempt to access a service from a different domain in Silverlight, you will notice that it fails. This is because, by default, a Silverlight application cannot call services that are on a different domain, unless it is permitted to do so by the service host. In order for Silverlight to determine if it has permission to access a service on a certain domain, it will look for one of two files in the root of the target domain: clientaccesspolicy.xml or crossdomain.xml.

First, Silverlight will look for a file named clientaccesspolicy.xml in the domain's root. This is Silverlight's client-access policy file. If you are publishing your own services that you want to be accessible by Silverlight applications, this is the file that you want to use, as it provides the most options for Silverlight application policy permissions. The following is a sample clientaccesspolicy.xml file:

<?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>

The important elements are <allow-from> and <grant-to>. The <allow-from> element defines which domains are permitted to access the resources specified in the <grant-to> element.

If Silverlight cannot find a clientaccesspolicy.xml file at the root of the domain from which you are attempting to access a service, it will then look for a file named crossdomain.xml in the root. This is the XML policy file that has been used to provide access for Flash applications to access cross-domain services, and Silverlight supports this file as well. The following is an example of a crossdomain.xml file:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy
   SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
  <allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>

Again, even though Silverlight supports crossdomain.xml, using clientaccesspolicy.xml for Silverlight applications is the preferred and best practice.

Accessing Data Through Sockets

In the majority of cases, your Silverlight applications will access data through web services. However, Silverlight provides another mechanism that, though rarely used, can be quite powerful. This mechanism is socket communications. In this section, you will look at a greatly simplified example of communicating with a server via sockets and TCP. The main purpose here is to give you a taste of using sockets in Silverlight so you have a basic understanding of the process and can consider whether you would like to take this approach. If so, you can refer to a more advanced resource, such as Pro Silverlight 4 in C# by Matthew MacDonald (Apress, 2010).

For this example, let's assume that you have a socket server running at the IP address 192.168.1.100 on port 4500. The socket server simply accepts text inputs and does something with them. In Silverlight, you want to connect to that socket server and send it text from a TextBox control.

First, you make a connection to the socket server. To do this, you create an instance of a System.Net.Sockets.Socket object for IP version 4 (AddressFamily.InterNetwork). The type will be Stream, meaning it will accept a stream of bytes, and the protocol will be TCP.

Socket socket;
socket = new Socket(
    AddressFamily.InterNetwork,
    SocketType.Stream,
    ProtocolType.Tcp);

You need to execute the socket's ConnectAsync() method, but first you must create an instance of SocketAsyncEventArgs to pass to the method, using a statement similar to the following:

SocketAsyncEventArgs socketArgs = new SocketAsyncEventArgs()
{
    RemoteEndPoint = new IPEndPoint(
        IPAddress.Parse("192.168.1.100"),
        4500)
};

This statement sets the target for the socket connection as 192.168.1.100 on port 4500.

In addition, since this is an asynchronous connection, you need to have notification when the connection has been established. To get this notification, you wire up an event handler to be triggered on the SocketAsyncEventArgs.Completed event. Once you have that wired up, you simply call the ConnectAsync() method, passing it your SocketAsyncEventArgs instance.

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

The method for this event handler will first remove the event handler, and then it will examine the response from the socket server. If it is successful, it will send a stream of bytes from your TextBox control to the socket server through your established connection.

void socketArgs_Completed(object sender, SocketAsyncEventArgs e)
{
    e.Completed -= socketArgs_Completed;

    if (e.SocketError == SocketError.Success)
    {
        SocketAsyncEventArgs args = new SocketAsyncEventArgs();
        args.SetBuffer(bytes, 0, bytes.Length);
        args.Completed += new EventHandler<SocketAsyncEventArgs>(OnSendCompleted);
        socket.SendAsync(args);
    }
}

Once again, since the calls to the socket are asynchronous, you wire up another event handler called OnSendCompleted, which will fire when your SendAsync() method is completed. This event handler will do nothing more than close the socket.

void OnSendCompleted(object sender, SocketAsyncEventArgs e)
{
    socket.Close();
}

Although this seems pretty simple, it is complicated by client-access policy permissions. In the same way that a Silverlight application can call a web service on a separate domain only if it has the proper client-access policy permissions, a Silverlight application can call a socket server only if that server contains the proper client-access policy permissions. The following is an example of a client-access policy for a socket server:

<?xml version="1.0" encoding ="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from>
        <domain uri="*" />
      </allow-from>
      <grant-to>
        <socket-resource port="4500-4550" protocol="tcp" />
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

Recall that when you're using a web service, the client-access policy is contained in afile named clientaccesspolicy.xml, which is placed in the domain's root. In a socket access situation, things are a bit more complex.

Before Silverlight will make a socket request to a server on whatever port is requested by the application, it will first make a socket request of its own to the server on port 943, requesting a policy file. Therefore, your server must have a socket service set up to listen to requests on port 943 and serve up the contents of the client-access policy in order for Silverlight applications to be able to make a socket connection.

Summary

In this chapter, you focused on accessing data from your Silverlight applications through WCF services. I also discussed accessing data from different domains and cross-domain policy files. In addition, you looked at using sockets in Silverlight from a high level.

In the next chapter, you will look at Silverlight's Navigational Framework.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset