# Saturday, August 07, 2010

Consuming OData from Silverlight 4 can be a very frustrating experience for people like me who are just now joining the Silverlight party.

When it doesn't work, it can fail silently and/or with incorrectly worded warnings.

This is a perfect example of where a blog post can hopefully fill in some of the gaps left by the Microsoft documentation.

First, let's setup a very simple Silverlight 4 application against the odata.org Northwind service.

Note: You may need to install the Silverlight 4 Tools for VS 2010 for this sample.
 http://www.bing.com/search?q=Microsoft+Silverlight+4+Tools+for+Visual+Studio+2010

In Visual Studio 2010:

File -> New -> Project
 Visual C# -> Silverlight -> Silverlight Application -> OK
  New Silverlight Application wizard
   Uncheck "Host the Silverlight application in a new website"
   Silverlight Version: Silverlight 4
   OK

Right Click References in Solution Explorer -> Add Service Reference...
 Address: http://services.odata.org/Northwind/Northwind.svc/
 Namespace: RemoteNorthwindServiceReference
 OK

From the toolbox, drag and drop a DataGrid from the "Common Silverlight Controls" section onto MainPage.xaml
 Change the AutoGenerateColumns property to True
 Change the Margin to 0,0,0,0
 Change the Width to 400

In MainPage.xaml.cs:
 Add the following using statement:

  using System.Data.Services.Client;

 Add the following class variable:

  private DataServiceCollection<RemoteNorthwindServiceReference.Shipper> _shippers;

 Add the following to the bottom of the MainPage constructor method:

  RemoteNorthwindServiceReference.NorthwindEntities remoteNorthwindService =
    new RemoteNorthwindServiceReference.NorthwindEntities(
      new Uri("http://services.odata.org/Northwind/Northwind.svc/"));

  _shippers = new DataServiceCollection<RemoteNorthwindServiceReference.Shipper>();
  _shippers.LoadCompleted +=
new EventHandler<LoadCompletedEventArgs>(_shippers_LoadCompleted);

  var query = from shippers in remoteNorthwindService.Shippers select shippers;
  _shippers.LoadAsync(query);

 Add the following new method:

  private void _shippers_LoadCompleted(object sender, LoadCompletedEventArgs e)
  {
    if (_shippers.Continuation != null)
    {
      _shippers.LoadNextPartialSetAsync();
    }
    else
    {
      this.dataGrid1.ItemsSource = _shippers;
      this.dataGrid1.UpdateLayout();
    }
  }

Debug -> Start Without Debugging
 You should get a dialog box titled "Silverlight Project" that says:
  "The Silverlight project you are about to debug uses web services.  Calls to the web service will fail unless the Silverlight project is hosted in and launched from the same web project that contains the web services.  Do you want to debug anyway?"
 Click "Yes"

This application runs fine.  The warning dialog message was obviously inaccurate and misleading.

I wish I would have understood what this warning dialog was trying to say, but since the remote OData service was working, I had no real choice but to ignore the dialog, which would haunt me when trying to use the same application to call a local OData service.

I believe this dialog is trying to warn you that Silverlight has special constraints when calling web services, but it's still not clear to me what those are.  Proceed cautiously.

Here is one way to successfully call a local OData webservice from Silverlight 4 in Visual Studio 10:

File -> New -> Project
 Visual C# -> Silverlight -> Silverlight Application -> OK
  New Silverlight Application wizard
   OK (Accept the defaults: ASP.NET Web Application Project, Silverlight 4, etc.)

Right Click SilverlightApplication<number>.Web in Solution Explorer -> Add New Item...
 Visual C# -> Data -> ADO.NET Entity Data Model -> Add
  Entity Data Model Wizard
   Next (Generate from database - this assumes you have Northwind running locally)
   New Connection... -> Point to your local Northwind Database Server -> Next
   Select the Tables check box (to select all tables) -> Finish

Right Click SilverlightApplication<number>.Web in Solution Explorer -> Add New Item...
 Visual C# -> Web -> WCF Data Service -> Add

In WcfDataService1.cs:
 Replace " /* TODO: put your data source class name here */ " with "NorthwindEntities" (no quotes)
 Add the following line to the InitializeService method:

  config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);

Debug -> Start Without Debugging

Right Click References under SilverlightApplication<number> in Solution Explorer -> Add Service Reference...
 Discover -> Services In Solution (note the port number of the discovered service, you will need it below)
 Namespace: LocalNorthwindServiceReference
 OK

Stop the browser that was started above

Follow the same basic steps as the first example above to modify MainPage except:
 Replace RemoteNorthwindServiceReference with LocalNorthwindServiceReference
 Replace "http://services.odata.org/Northwind/Northwind.svc/" with "http://localhost:<port number noted above>/WcfDataService1.svc/"

Debug -> Start Without Debugging

This application runs fine (without the warning dialog this time).

It can be ridiculously difficult to get a local OData service working with Silverlight 4 if you don't carefully dance around the project setup issues.
The warning dialog when you start debugging is nearly useless and the app will return no data with no apparent errors if you setup the project incorrectly.

Some references I found useful while building this sample:

 MSDN: How to: Create the Northwind Data Service (WCF Data Services/Silverlight)
 http://msdn.microsoft.com/en-us/library/cc838239(VS.95).aspx

 Audrey PETIT's blog: Use OData data with WCF Data Services and Silverlight 4
 http://msmvps.com/blogs/audrey/archive/2010/06/10/odata-use-odata-data-with-wcf-data-services-and-silverlight-4.aspx

 Darrel Miller's Bizcoder blog: World’s simplest OData service
 http://www.bizcoder.com/index.php/2010/03/26/worlds-simplest-odata-service/

Once you deploy your OData service to a real web hosting environment, you'll likely need to do some special setup to access your OData service from Silverlight 4 (clientaccesspolicy.xml / crossdomain.xml):

 Making a Service Available Across Domain Boundaries
 http://msdn.microsoft.com/en-us/library/cc197955(VS.95).aspx

Tremendous thanks go out to Scott Davis of Ignition Point Solutions for pointing me to the flawed project setup as the reason I couldn't get this working initially.