Silverlight and ADO.NET Data Services

Someone mailed me to ask whether I had a video on how to put together Silverlight and ADO.NET Data Services.

I don’t at the time of writing and I’ve also got a cold right now ( thank you, Microsoft Manchester office 🙂 ) so I thought I’d write something rather than record it.

Let’s run through a step-by-step thing.

Visual Studio 2008 – File->New->Web Site. I’m going for the filesystem and C# as below;

image

Now, to make it easy to work with ADO.NET Data Services, I’m going to add in an ADO.NET Entity Data Model for Northwind. That is … Website->Add->New Item;

image

I say “yes” to add it to my app_code folder, then select;

image imageimage

and then I can go and add a new ADO.NET Data Service ( again via Website->Add New Item );

image

and then I can update my service code to read;

public class Service : DataService<NorthwindEntities>
{
  // This method is called only once to initialize service-wide policies.
  public static void InitializeService(IDataServiceConfiguration config)
  {
    config.SetEntitySetAccessRule("*", EntitySetRights.All);
  }
}

and I’m also going to reconfigure my project so that the built-in web server uses a particular port rather than a randomly selected one by selecting the website’s properties and changing it;

image

Then I can add a new Silverlight 2 project here by doing File->Add New Project;

image

and then accepting the defaults for the Silverlight options;

image

Now, I need my Silverlight project to call the ADO.NET Data Service from my other project so I do a quick “view on browser” on my Service.svc file just to spin up the web server. Then I can drop to a command line in my c:\demo\BlogPostSL folder and use the datasvcutil.exe tool to generate some proxy code for my Silverlight project;

image

Then I can pop back to Visual Studio and add this as an existing item to my Silverlight project. I also need to add a reference to;

System.Data.Services.Client

in order to make that project ( with the new proxy code ) compile.

Now, I want to build a little bit of UI for Silverlight to display some data. I want to use the DataGrid so the first thing that I need to do is to add a reference to pick up the DataGrid assembly as well. So, that’s another reference to the Silverlight project;

System.Windows.Controls.Data

Now I can go and edit some XAML and build up a little piece of UI;

<UserControl
  x:Class="BlogPostSL.Page"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data">
  <Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
      <RowDefinition
        Height="8*" />
      <RowDefinition />
      <RowDefinition />
    </Grid.RowDefinitions>
    <data:DataGrid
      x:Name="dataGrid"
      Margin="10"
      AutoGenerateColumns="True"
      ItemsSource="{Binding}" />
    <Button
      Margin="10"
      Content="Get Data"
      Grid.Row="1"
      Click="OnGetData" />
  </Grid>
</UserControl>

and then drop some code behind it;

  public partial class Page : UserControl
  {
    public Page()
    {
      InitializeComponent();

      this.Loaded += OnLoaded;
    }
    void OnLoaded(object sender, RoutedEventArgs e)
    {
      proxy = new NorthwindEntities(
        new Uri("http://localhost:32767/BlogPost/Service.svc", UriKind.Absolute));
    }
    void OnGetData(object sender, EventArgs args)
    {
      // I don't think that we can use LINQ here because there's no way then
      // to make it asynchronous. So, let's use the manual way of doing things.
      DataServiceQuery<Customers> query =
        proxy.CreateQuery<Customers>("Customers?$filter=Country eq 'UK'");

      query.BeginExecute((asyncResult) =>
        {
          try
          {
            IEnumerable<Customers> result = query.EndExecute(asyncResult);

            // Doubt if we're on the UI thread so...
            Dispatcher.BeginInvoke(() =>
              {
                ObservableCollection<Customers> data = new ObservableCollection<Customers>();
                foreach (Customers c in result)
                {
                  data.Add(c);
                }
                dataGrid.DataContext = data;
              });
          }
          catch (Exception ex)
          {
            // TODO
            Debugger.Break();
          }
        }, null);
    }
    NorthwindEntities proxy;
  }

and now clicking on my “Get Data” button gets me some data;

image

Now, that Orders column isn’t really something that I want so I guess the easiest thing to do there is to hide it. I still want the grid to automatically generate columns though so perhaps I can just get this one removed?

    public Page()
    {
      InitializeComponent();

      this.Loaded += OnLoaded;

      dataGrid.AutoGeneratingColumn += OnGeneratedColumn;
    }
    void OnGeneratedColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
    {
      if (e.Property.Name == "Orders")
      {
        e.Cancel = true;
      }
    }

That’s a bit better in that I’ve got some data on the screen.

As an aside, where I said in that code above that we “can’t write this query with LINQ”. That wasn’t strictly true. I can write something like;

      var query = (DataServiceQuery<Customers>)
        from c in proxy.Customers
        where c.Country == "UK"
        select c;

      query.BeginExecute((asyncResult) =>
        { // .......

and that looks to work fine once you get over the cast.

I’ll look at updating, inserting, deleting in subsequent posts otherwise this gets very long very quickly.