Creating Silverlight Applications (WCF Data Services)

Important

The WCF Data Services 5.0 client library for Silverlight is not supported for Windows Phone applications. You must instead use the OData Client Library for Windows Phone, which is included in the Windows Phone 7.1 SDK. For more information, see Open Data Protocol (OData) Overview for Windows Phone. There is currently no client library for Windows Phone that supports OData v3.

The WCF Data Services client library for Silverlight generates HTTP requests to a data service that supports version 3 of the OData protocol and transforms the data in the OData response feed into objects on the client. The two main classes of the client library are the DataServiceContext class and the DataServiceQuery<TElement> class. The DataServiceContext class encapsulates operations that are executed against a specified data service. OData-based services are stateless. However, the DataServiceContext maintains the state of entities on the client between interactions with the data service. This enables the client to support features such as change tracking and identity management. The DataServiceQuery<TElement> class represents a query against a specific entity set. For more information, see Using a Data Service in a .NET Framework Application (WCF Data Services). For a live example of an application that consumes a feed from the Northwind sample data service, see the WCF Data Services QuickStart for Silverlight

Note

When using the WCF Data Services client library for Silverlight, all requests to the service are made asynchronously. For more information, see Asynchronous Operations (WCF Data Services).

This topic contains the following sections:

Generating Client Data Service Classes

You can use the Add Service Reference dialog box in Visual Studio to add a reference to any service that exposes an OData feed. For more information, see Generating the Client Data Service Classes (WCF Data Services). The client data service classes can also be generated by using the DataSvcUtil.exe tool at the command prompt. For more information, see How to: Manually Generate Client Data Service Classes (WCF Data Services).

Note

When the WCF Data Services 5.0 release is installed, the Add Service Reference tool will automatically add a reference to the Microsoft.Data.Services.Client.SL.dll version of the client library instead of adding a reference to the System.Data.Services.Client.dll version that is included in Silverlight. If for some reason you need to instead use the previous version of the Silverlight client, you should manually add a reference to the Silverlight version of the client library. For more information, see How to: Manually Generate Client Data Service Classes (WCF Data Services).

Accessing and Changing Resources

In a Silverlight-based application, all operations against a data service are asynchronous. You perform asynchronous operations by using pairs of methods on the DataServiceContext and DataServiceQuery<TElement> classes that start with Begin and End respectively. The Begin methods register a delegate that the service calls when the operation is completed. The End methods should be called in the delegate that is registered to handle the callback from the completed operations. When you call the End method to complete an asynchronous operation, you must do so from the same DataServiceQuery<TElement> or DataServiceContext instance that you used to begin the operation. Each Begin method takes a state parameter that can pass a state object to the callback. This state object is retrieved as the IAsyncResult that is supplied with the callback and is used to call the corresponding End method to complete the asynchronous operation. For example, when you supply the DataServiceQuery<TElement> instance as the state parameter when you call the BeginExecute method on the instance, the same DataServiceQuery<TElement> instance is returned as the IAsyncResult. This instance of DataServiceQuery<TElement> is then used to call the EndExecute method to complete the query operation. For more information, see Asynchronous Operations (WCF Data Services).

Because the WCF Data Services client library for Silverlight accesses the data service asynchronously by using network protocols, you must use the BeginInvoke method of the Dispatcher class to correctly marshal the response operation back to the main application thread (the UI thread) of your Silverlight-based application. For more information, see Synchronizing Data for Multithreading.

Querying Resources

The WCF Data Services client library for Silverlight enables you to execute queries against an OData data service by using familiar .NET Framework programming patterns that include using Language Integrated Query (LINQ). When the BeginExecute method on DataServiceQuery<TElement> or DataServiceContext is called, the client library translates a query or Uniform Resource Identifier (URI) into an HTTP GET request message. The client library receives the corresponding response message and translates it into instances of client data service classes. These classes are tracked by the DataServiceContext to which the DataServiceQuery<TElement> belongs. For more information, see How to: Execute Asynchronous Data Service Queries (WCF Data Services).

In some scenarios, it is helpful to know the total number of entities in an entity set and not merely the number in the feed returned by the query. Call the IncludeTotalCount method on the DataServiceQuery<TElement> to request that this total count of entities in the set be included with the query result. In this case, the TotalCount property of the returned QueryOperationResponse<T> returns the total number of entities in the set. You can also use the AddQueryOption method to add any of the other query options supported by OData to a query. For more information, see Querying the Data Service (WCF Data Services)

LINQ Queries

Because the DataServiceQuery<TElement> class implements the IQueryable<T> interface defined by LINQ, the WCF Data Services client library for Silverlight is able to transform LINQ queries against entity set data into a URI that represents a query expression evaluated against a data service resource. For example, the following LINQ query returns a feed that is a collection of Order entities filtered by the CustomerID property value supplied by the user in the customerId text box.

' Define a query that returns orders for a give customer.
Dim query = From orderByCustomer In context.Orders _
                Where orderByCustomer.Customer.CustomerID = _
                Me.customerId.Text _
                Select orderByCustomer
// Define a query that returns orders for a give customer.
var query = from orderByCustomer in context.Orders
            where orderByCustomer.Customer.CustomerID == this.customerId.Text
            select orderByCustomer;

Loading Deferred Content

By default, WCF Data Services limits the amount of data that a query returns. However, you can explicitly load additional data, including related entities, paged response data, and binary data streams, from the data service when it is needed. When you execute a query, only entities in the addressed entity set are returned. For example, when a query against the Northwind data service returns Customers entities, by default the related Orders entities are not returned, even though there is a relationship between Customers and Orders. Related entities can be loaded with the original query (eager loading) or on a per-entity basis (explicit loading). For more information, see Loading Deferred Content (WCF Data Services). When using the Silverlight client and DataServiceCollection<T>, you can load collections of related entities from a navigation property by calling LoadAsync.

Tip

When deciding on a pattern for loading related entities, consider the performance tradeoff between message size and the number of requests to the data service.

When paging is enabled in the data service, you must explicitly load subsequent data pages from the data service when the number of returned entries exceeds the paging limit. Because it is not possible to determine in advance when paging can occur, we recommend that you enable your Silverlight client application to properly handle a paged OData feed. For examples of how to use handle a paged response, see How to: Bind Data Service Data to Controls (Silverlight Client) and Querying the Data Service (WCF Data Services).

Query Projection

Projection provides a mechanism to reduce the amount of data in the OData feed returned by a query by specifying that only certain properties of an entity are returned in the response. For more information, see OData: Select System Query Option ($select). You can add a projection clause to a LINQ query by using the select clause (Select in Visual Basic). Returned entity data can be projected into either entity types or non-entity types on the client. Changes made to non-entity types cannot be saved to the data service. For example, the following LINQ query projects Customer data into a new CustomerAddress entity type on the client.

Dim query = From c In context.Customers _
                    Where c.Country = "Germany" _
                    Select New CustomerAddress With
                        {.CustomerID = c.CustomerID, _
                         .Address = c.Address, _
                         .City = c.City, _
                         .PostalCode = c.PostalCode, _
                         .Country = c.Country _
                        }
var query = from c in context.Customers
            where c.Country == "Germany"
            select new CustomerAddress
            {
                CustomerID = c.CustomerID,
                Address = c.Address,
                City = c.City,
                PostalCode = c.PostalCode,
                Country = c.Country
            };

Important

Data loss might occur in the data service when you save updates that were made to projected types. For more information, see Projection Considerations in the WCF Data Services client documentation.

For more information, see How to: Project Data Service Query Results (Silverlight Client).

Modifying Resources and Saving Changes

The client tracks changes to entities that you report by manually executing the following methods on DataServiceContext:

These methods enable the client to track added and deleted entities and also changes that you make to property values or to relationships between entity instances. When you use the Add Service Reference dialog to generate client data service classes, an AddTo method is also created for each entity in the generated DataServiceContext class. Use these methods to add a new entity instance to an entity set and report the addition to the context. Those tracked changes are sent back to the data service asynchronously when you call the BeginSaveChanges and EndSaveChanges methods.

When you add a new entity by using either the AddObject method or the appropriate AddTo method, any relationships between the new entity and related entities are not automatically defined. You can create and change relationships between entity instances and have the client library reflect those changes in the data service. Relationships between entities are defined as associations in the model, and the DataServiceContext tracks each relationship as a link object in the context. WCF Data Services provides the following methods on the DataServiceContext class to create, modify, and delete these links:

For more information, see Updating the Data Service (WCF Data Services)

Working with Binary Data

OData defines a mechanism for accessing binary data separate from an entity to which it belongs. In this way, an OData service can expose large binary data as a media resource that belongs to a media link entry. The WCF Data Services client for Silverlight can consume a media resource from an OData service as a binary stream. To access the binary stream, call the BeginGetReadStream method on the DataServiceContext instance that is tracking the entity that is the media link entry. This asynchronous method returns a DataServiceStreamResponse object when the EndGetReadStream method is called on the DataServiceContext instance returned by the callback. Likewise, a media resource is sent to the OData service when you call the SetSaveStream method and after the BeginSaveChanges and EndSaveChanges methods are called. For more information, see How to: Access Binary Data as a Stream (Silverlight Client).

Data Binding

The WCF Data Services client for Silverlight supports binding of data to controls by using the DataServiceCollection<T> class. This class, which inherits from ObservableCollection<T>, represents a dynamic data collection that provides notifications when items get added to or removed from the collection. These notifications enable the DataServiceContext to track changes automatically without your having to explicitly call the change tracking methods. A DataServiceCollection<T> is defined based on a DataServiceQuery<TElement>. This query, when it is executed, provides the objects for the collection.

The LoadAsync method is used to asynchronously execute the query and load the results to the collection. This method ensures that the results are marshaled to the correct thread, so you do not need to use a Dispatcher. When you use an instance of DataServiceCollection<T> for data binding, the client ensures that objects tracked by the DataServiceContext remain synchronized with the data in the bound UI element. You do not need to manually report changes in entities in a binding collection to the DataServiceContext. For more information, see How to: Bind Data Service Data to Controls (Silverlight Client).

Cross-Domain Execution

Silverlight enables you to access services hosted in a separate domain. This type of access must be explicitly enabled by deploying a cross-domain policy file on the server. This functionality is included in the Silverlight client HTTP implementation.

Note

There are important security considerations before you allow Silverlight clients to access Web services in a cross-domain situation. For more information, see HTTP Communication and Security with Silverlight.

For most requests to a data service, the WCF Data Services client for Silverlight uses an XMLHTTP implementation. However, when the WCF Data Services client detects a cross-domain request, it automatically switches to use the Silverlight client HTTP implementation.

Note

The client will automatically switch the HTTP implementation only when the HttpStack property is set to Auto.

For an example of how to configure a data service to allow cross-domain requests from a Silverlight-based application, see the post Using the ADO.NET Data Services Silverlight Client in X-Domain and Out-of-Browser Scenarios. Support for cross-domain execution is new in Silverlight 4.

Out of Browser Execution

You can configure Silverlight-based applications so that users can install them from their host Web pages and run them outside the browser. The WCF Data Services client for Silverlight supports out-of-browser execution. When the WCF Data Services client detects that an application is running outside the browser, it automatically switches to use the Silverlight client HTTP implementation. This is the same behavior that occurs with cross-domain execution; however a cross-domain policy file is not required. For more information, see Out-of-Browser Support.

Client Authentication

By default, the WCF Data Services client for Silverlight makes requests to the data service by using the same client credentials as the Web browser, and authentication is managed by the Web browser. However, when accessing the data service requires a cross-domain request or when the Silverlight application runs outside of the Web browser, you are able to specify the credentials supplied when making the request. In these scenarios, the client automatically uses the Silverlight client HTTP implementation to make requests and default credentials are used for authentication. However, when the UseDefaultCredentials property is set to false, the client uses the ICredentials assigned to the Credentials property when authenticating with the data service.

Warning

User credentials should only be requested during execution and should not be cached. Credentials must always be stored securely.

You can also supply credentials by requiring that the application use the Silverlight client HTTP implementation. To do this, you must set the value of the HttpStack property to ClientHttp. The following code ensures that credentials collected from the user during execution are used when accessing the data service:

' Select the client HTTP stack and set the credentials.
context.HttpStack = HttpStack.ClientHttp
context.UseDefaultCredentials = False
context.Credentials = _
    New NetworkCredential(userName, password, domain)
// Select the client HTTP stack and set the credentials.
context.HttpStack = HttpStack.ClientHttp;
context.UseDefaultCredentials = false;
context.Credentials = 
    new NetworkCredential(userName, password, domain);

When the Silverlight client HTTP implementation is not used, setting the value of the UseDefaultCredentials property to false will cause an exception to be raised during execution. When the value of the UseDefaultCredentials is true, the default credentials are used, even if the Credentials property is set.

Warning

Data sent with Basic and Digest Authentication is not encrypted, so the data can be seen by an adversary. Additionally, Basic Authentication credentials (user name and password) are sent in the clear and can be intercepted.

For more information, see How to: Specify Client Credentials for a Data Service Request (Silverlight Client). For an example of how to access a data service that uses ASP.NET forms authentication from a Silverlight application, see the article Using the ADO.NET Data Services Silverlight Client Library in X-Domain and Out-of-Browser Scenarios – II (Forms Authentication).