Consuming an ADO.NET Data Service from Silverlight

When developing Silverlight line of business (LOB) applications we often need to get at some data from a database. There are various ways to do so. One option that is easy to get started with with is creating an ADO.NET Entity Data Model (EDM) and exposing that entity model using an ADO.NET Data Service.

 

Getting started

The first thing we need to create is an ASP.NET Web Application. This is basically going to act as a container for our Data Service and our Silverlight LOB application. As you can see below I named the Web Application SLDataService.

image

Next we need to add the ADO.NET Entity Data Model. I already have a Northwind SQL Server database on my machine and that is the database I am going to expose to I am naming the EDM Northwind.edmx.

image

When the popup ask if I want to create a new EDM based on an existing database or start from scratch I selected the existing database option and pointed it at my Northwind database. This lets me select the tables I want to import. In this case I am only interested in the Customers table so that is what I am importing. By default the class name will be the same as the table name, i.e. Customers, but as Customer is  a more logical name I removed the training “s”.

image

Next step is creating the ADO.NET Data Service to publish the entity model. Add another item to the project, this time choosing an ADO.NET Data Service and naming it Northwind.svc.

image

If we open the Northwind.svc.cs file we need to enter a few details before we can proceed. The first thing we need to enter is EDM class name we are going to expose. In this case that is the NorthwindEntities model. Next we need to allow callers to access the EDM. By default nothing is exposed to this is a crucial step that needs to be done in the InitializeService() function! In this case I am going to allow everything, maybe not the best solution in real live [:)]

public class Northwind : DataService<NorthwindEntities>
{
    public static void InitializeService(IDataServiceConfiguration config)
    {
        config.SetEntitySetAccessRule("*", EntitySetRights.All);
    }
}

Testing our data service

Now we should be able to test our ADO.NET Data Service in the browser. Start the project and you should see the following in the browser.

image

Navigate to the following URL http://localhost:4163/Northwind.svc/CustomerSet by adding CustomerSet and you should see the following customer data.

image

If you see an RSS feed instead of the XML go into settings and disable the feed reading view. BTW the reason the customers data is showing up as an RSS feed is because the ADO.NET Data Services uses the ATOM feed format to transmit its data using a REST approach.

image

 

Creating a Silverlight LOB client

The next step is to create a Silverlight client application to consume and display the data. Add a new project the solution of type Silverlight Application and name it SilverlightClient.

image

When prompted just accept the defaults and add the Silverlight application to the existing SLDataService app. Next we need to add a service reference to the Northwind ADO.NET Data Service. Call it NorthwindService.

Next we need to set up a bit of a user interface to display the list of customers. I kept this really simple and used a ListBox with a DataTemplate like this:

<UserControl x:Class="SilverlightClient.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="500" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <ListBox x:Name="lstCustomers">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding CompanyName}" Width="250" />
                        <TextBlock Text="{Binding City}" Width="100" />
                        <TextBlock Text="{Binding Country}" Width="100" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</UserControl>

 

The final step is to load the data from the service and bind this to the ListBox. To do that we need to create an DataServiceContext object of type NorthwindEntities. Unlike a regular service reference the DataServiceContext does not know its own URL by default and we need to be given the URL where the web service is located. Easy enough using the URL of the current page, loaded using HtmlPage.Document.DocumentUri, and the name of the service, Northwind.svc in this case.

Uri uri = new Uri(HtmlPage.Document.DocumentUri, "Northwind.svc");
NorthwindEntities context = new NorthwindEntities(uri);

Using this context object we can do a LINQ query to load some data. Pretty standard stuff except that the asynchronous nature of Silverlight changes the actual loading of the data a bit. Normally you would expect to do something like:

var query = from cust in context.CustomerSet 
            select cust;

lstCustomers.ItemsSource = query.ToList();

In this case that won’t work and will only result in an System.NotSupportedException with the following message "Specified method is not supported.".

The correct way of doing this is by using a DataServiceQuery object which can load the data asynchronously as follows:

var query = from cust in context.CustomerSet 
            select cust;

var dsQuery = (DataServiceQuery<Customer>)query;
dsQuery.BeginExecute(
    result => lstCustomers.ItemsSource = dsQuery.EndExecute(result), 
    null);

And the final result looks like this

image

 

Not bad at all [:)]

The complete code behind looks like this:

using System;
using System.Data.Services.Client;
using System.Linq;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using SilverlightClient.NorthwindService;

namespace SilverlightClient
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(Page_Loaded);
        }

        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            Uri uri = new Uri(HtmlPage.Document.DocumentUri, "Northwind.svc");
            NorthwindEntities context = new NorthwindEntities(uri);
 
            var query = from cust in context.CustomerSet 
                        select cust;

            // This doesn't work and will throw an System.NotSupportedException!
            // lstCustomers.ItemsSource = query.ToList();

            var dsQuery = (DataServiceQuery<Customer>)query;
            dsQuery.BeginExecute(
                result => lstCustomers.ItemsSource = dsQuery.EndExecute(result), 
                null);
        }
    }
}

Visual Studio project sampe code

Enjoy!

[f1]

[f2]

13 thoughts on “Consuming an ADO.NET Data Service from Silverlight

  1. @Alessandro

    I am afraid that doesn’t work in VB as you can only use single statements that return something. You will need to use a separate function and pass the dsQuery as the parameter.

  2. Did you try to secure this code?
    I’m lookin for a possibility to call the Data Service in Silverlight only for an login user.

    Thanks

  3. Thanks for this example. I have a question that I’m struggling with:

    When assembling an Entity for storage I need to get a related value from a lookup file. For example a person has a status code assigned of ‘Pending’ which is in a table called StatusCodes.

    In the Entity Framework, I need to set the value of person.StatusCode equal to an instance of the StatusCode. In the Entity Framework or in LINQ2Sql I’d so something like this:

    var person = Person.CreatePerson( stuff );
    var statCode = myContext.StatusCodeSet.Where(sc => sc.Description == “Pending”).FirstOrDefault();
    person.StatusCode = statCode;
    // …more code here…

    myContext.BeginSaveChanges(SaveChangesOptions.Batch,
    new AsyncCallback(OnSaveAllComplete),
    null);

    As you mentioned the query for the statCode won’t work.

    However,

    var person = Person.CreatePerson( stuff );
    var query = from stat in myContext.StatusCodeSet
    where stat.Description == “Pending”
    select stat;
    var dsQuery = (DataServiceQuery)query;
    dsQuery.BeginExecute(
    result => tutorApplication.StatusCode = dsQuery.EndExecute(result).FirstOrDefault(), null);

    // …more code here…

    myContext.BeginSaveChanges(SaveChangesOptions.Batch,
    new AsyncCallback(OnSaveAllComplete),
    null);

    doesn’t work either due to the Async nature of the query.

    Do you have a suggestion about how to handle this situation?

    Thanks

  4. No data is retrieved. I’m using VWD Express 2008
    with SSMS 2008.

    How do I turn off the RSS feeds ? Can you include a copy of the Northwind.mdf for this sample ?
    I’m getting a version error.

    Thanks

  5. Hi!

    ADO.NET Data Services returns this message:

    “Internet Explorer cannot display this feed.
    The size of this feed exceeds the download limit.”

    Maybe because I’am accessing a large data.
    How can I resolve this?

    Thanks.

  6. if there are more number of records in a table the select query is throwing an exception (i think the buffer size needs to be increased as in WCF service) can any any one know the solution.

Leave a Reply

Your email address will not be published. Required fields are marked *