Using ADO.NET Data Services in Silverlight 2 Beta 2

ADO.NET Data Services

UPDATE: I had the PUT/POST reversed.  It reads correctly now.  (Thanks to commenter Rob for pointing it out).

Now that Silverlight 2 Beta 2 has launched, we have the ability to consume ADO.NET Data Services (formerly Astoria) from within Silverlight projects. ADO.NET Data Services are a perfect match for client-side technologies like Silverlight and ASP.NET AJAX.

For the uninitiated, ADO.NET Data Services is a new part of the .NET 3.5 framework that supports exposing a data model (e.g. LINQ for SQL, Entity Framework, etc.) as a set of queryable REST endpoints. ADO.NET Data Services maps the four data verbs into the four HTTP verbs:

  • Create == POST
  • Read == GET
  • Update == PUT
  • Delete == DELETE

Essentially it provides a way to use a data model across the firewall. It works by exposing IQueryable endpoints through a URI-based syntax allowing developers control over how the data is retrieved through:

  • Filtering
  • Sorting
  • Paging
  • Shaping

In addition ADO.NET Data Services utilizes JSON and Atom (though Plain Old XML may be supported eventually) as the serialization formats. These make it easy to consume in client-side interfaces like Silverlight and AJAX. For more information on ADO.NET Data Services, see there site:

http://msdn.microsoft.com/en-us/data/bb931106.aspx

Before you can consume a Data Service in Silverlight, you will need to create a model and service.  See Guy Burstein's walkthrough of creating the service here:

http://blogs.microsoft.co.il/blogs/bursteg/archive/2008/05/12/visual-studio-2008-sp1-ado-net-data-service-walkthrough.aspx

You *can* use LINQ to SQL as your data model but currently it does not have support for updating via ADO.NET Data Services so if you need to read and write data, you should start with an Entity Framework data model.

In Silverlight 2 Beta 2, ADO.NET Data Services is composed of an in-memory library allows asynchronous LINQ queries that are translated into the URI syntax automatically. Before you can get started you will need a a client-side version of the Data Context object and data contract classes for the entities in your data model. To do this, simply use the DataSvcUtil.exe tool located in the C:\Windows\Microsoft.NET\Framework\v3.5\ directory. Typically you would call it by specifying the URI of the service, the name of the class file to create and what language to use:

DataSvcUtil.exe /uri:http://localhost:8888/YourService.svc 
                /out:dataclass.cs 
                /language:CSharp

Once that class is created, add it to your Silverlight project.  You will also need to add a reference to the System.Data.Client.Services.dll assembly. Now we are ready to get some data. 

First we need to create an instance of the context class.  This class exposes each of the model types as queryable properties.  When creating an instance of the class, you must specify a URI to the service itself:

NorthwindEntities ctx = 
  new NorthwindEntities(new Uri("/Products.svc", UriKind.Relative));

Querying data with Data Services looks much like any other LINQ-based query:

var qry = from p in ctx.Products
          orderby p.ProductName
          select p;

Normally, you could execute the query directly by calling something like ToList() against the query.  Since that operation would cause a synchronous web request to happen across the network, that isn't supported in Silverlight 2 Beta 2.  In fact, if you try you will simply get a NotImplemented exception. In order to execute these queries, you will need to cast the query into a DataServiceQuery.  The DataServiceQuery allows you to call BeginExecute to start an asynchronous query as seen below:

// Cast the query to a DataServiceQuery
DataServiceQuery<Product> productQuery = 
                                  (DataServiceQuery<Product>)qry;

// Start the execution
productQuery.BeginExecute(new AsyncCallback(OnLoadComplete), 
                          productQuery);

Once the query completes, it will call the OnLoadComplete method that was specified in the AsyncCallback.  In this method, you first grab the DataServiceQuery that you sent with the request then end the execution to retrieve the results as seen below:

void OnLoadComplete(IAsyncResult result)
{
  // Get a reference to the Query
  DataServiceQuery<Product> productQuery = 
    (DataServiceQuery<Product>)result.AsyncState;

  // Get ther esults and add them to the collection
  List<Product> products = productQuery.EndExecute(result).ToList();
}

While its not as straightforward as synchronous execution, the new ADO.NET Data Services certainly works well in Silverlight 2 Beta 2 and provides a great way to use existing or planned data models over Internet applications. There are a couple of caveats:

  • Error handling and communication is very confusing right now as most real errors are being swallowed by the server instead of communicating back to the client. To find out what is really happening, use of Fiddler and enabling breaking on the throwing of all .NET exceptions will help a lot.
  • There are bugs on updating data that may get in your way.  Using Batch Saves will solve most of these issues.
  • Currently the Data Contract objects do not support INotifyPropertyChanged or INotifyCollectionChanged so data binding may be affected in some cases.There are partial methods for detecting changes to specific properties which makes implementing the INotifyPropertyChanged interface trivial, but not quick. Future versions (e.g. post-RTM) will give us better control over the generation of the data contract classes.
  • There is no built-in Visual Studio support to build the data contract/context classes.  This is also coming soon, though the exact schedule is unknown.

I am currently working on two examples for this (a simple one and a more complex one) as well as an article for MSDN, all of which will greatly expand the details of how to use ADO.NET Data Services (including how to save changes back to the server. Be sure to watch here for details of those samples and articles.

Comments:

Hi Shawn;
Thanks for the Astoria samples. I was running your "Simple" app and I changed the unit price of "One" item an hit "SaveAll". But I noticed it took a long time to save one record. My question is, are you traversing through all the records in the grid to see which one was changed or do you flag the "Changed" one and only pass to Astoria as the "Changed" record?

Or is it Astoria that is that slow to update one record?

Thanks!
..Ben

Ben,

My site's upload speeds are just really slow. In fact, I am monitoring changes to mark items as dirty (so that the context object can update them for us). Look out for my upcoming MSDN article that talks much more in depth about strategies for optimizing this.

Also, this version of ADO.NET Data Services' Silverlight Client Library is pretty rough. The error handling is fairly unusable and there are some bugs, so this is the time to start playing with it to get used to it, but it is *not* ready for production yet.

Shawn

Shawn, Thanks for quick reply;

Yes, I do intent to use Astoria fully and I'll be monitoring your articles. Please make note on your main page when that MSDN article is out.
Plus, we can really use some samples on Astoria and SL.

Thanks again!

In addition to all those Expectations you have mentioned, I want to add a "Support for RTL Languages" too, which I'm sure you all are in agree!
Thanks for the article, it was really helpful to me.
Mohammad Porooshani

Hi,

Do you have PUT and POST the right way around up there?

I thought CREATE==POST and UPDATE==PUT? Tracing with Fiddler on my machine shows that INSERT==POST?

Thanks,

Mike.

INSERT does == POST...In CRUD, the "C" in CRUD is Create which translates into a SQL INSERT.

Firstly, thanks for the demo you seem to be at the forefront of this stuff pumping out demos b4 the others. I tried your demo and northwind products and noticed it updates fine but tried to see if concurrency is handled but it does not appear to be. Is this because the new 'bits' (ADO.net data services and SL beta2) don't have the ability to handle concurrency yet or have you just decided to not check for it in your demo. Thanks again Steve.

Data Services doesn't actually do any of this. Its job is to provide the layer over the model. In this case I am using Entity Framework (mostly because its the only model that supports the IUpdateable interface that is required, but it will come for other models soon). So its actually Entity Framework that isn't handling the concurrency.

Hi,

In Silverlight 1.1 we could use the "LoadProperty(user, "Roles")" methot to get the associated roles of a user (In a context of the following schema tables : User, Role, UserRole)

This method doesn't exist anymore, could you explain how to get the "associations" objects of User in SL2B2 ?

Sorry for my bad english :/

Thanks in advance

We are looking to use the entity framework model. However I am looking for some guidance on how this might work. Currently our application has a web server which receives requests. Some of these requests run via sync calls while others run via async calls (as specified in the data of the request. We have a separate async server which looks at an sql table to monitor to see if a new async request has arrived. This server then passes this request to the database server and awaits results. Can anyone discuss if having this separate async server is possible using the entity framework model. How might we extend the parameters that can be passed to the entity framework. such as adding $requesttype like filter. thanks for any advice. rick

btw my email is rwatson@ogleveeltd.com if anyone might have suggestions or a design pattern regarding my earlier post

Your blog entry is incorrect. It should read

CREATE == POST
UPDATE == PUT

Rob,

Yup, I always get them reversed...fixed.

Hi Shawn

Downloaded the sample app from www.silverlightdata.com.
Managed to get my NHibernate dependencies sorted out so the project builds fine now. However I'm getting a cast exception in the Silverlight client when I run from NHibernate.aspx. Unable to cast object of type DataServiceOrderedQuery to System.Data.Services.Client.DataServiceQuery... ?

I rebuilt the proxy using DataSvcUtil.exe /uri:http://localhost:8529/NHProductService.svc /out:ProductModel.partial.cs /language:CSharp but got a bunch of errors on the FirePropertyChanged event handlers?

Cool project.. and an excellent demo.. (would love to get it to run)

Just wanted to point out incompatibility issues with SL2Beta2 and 3.5SP1 Ado.Net DataServices:
http://forums.microsoft.com/msdn/ShowPost.aspx?PostID=3736840&SiteID=1
Workaround:
http://blogs.msdn.com/astoriateam/archive/2008/09/02/interim-release-making-sl2-beta-2-clients-work-with-net-fx-3-5-sp1-rtm-servers.aspx


 



 
Save Cancel