OData v2 and Windows Phone 7

Posted on October 30, 2010 by

panoramaYesterday at PDC10, Scott Guthrie demonstrated a Windows Phone 7 application that he was building using Silverlight.  During this time, he mentioned that a new OData stack had been released for .NET which included an update to the library for Windows Phone 7. Now you might think that this was just a regular old upgrade – you know… bug fixes, optimizations, etc.  It was, but it also signaled a rewrite of the existing CTP that had been available for Windows Phone 7.  In this rewrite, there are some important feature changes that you need to be aware of, as announced by Mike Flasko on the WCF Data Services Team Blog yesterday:

LINQ support in the client library has been removed as the core support is not yet available on the phone platform.  That said, we are actively working to enable this in a future release.  Given this, the way to formulate queries is via URIs. We’ve added a LoadAsync(Uri) method to the DataServiceCollection class to make it simple to query services via URI. So you can easily support the phone application model we’ve added a new standalone type ‘DataServiceState’ which makes it simple to tombstone DataServiceContext and DataServiceCollection instances

In this post, I’ll go through the first 2 of the changes explain what they mean to you, and show you how to adapt your application to use the new library.  In a future post, I’ll explore the new support for the phone application model and tombstoning.

Where are the new bits?

Firs things first – we need to get our hands on the new bits.  There are 3 new download packages available from the CodePlex site for OData:

  • OData for .NET 4.0, Silverlight 4 and Windows Phone 7 (Source Code, if you need it)
  • OData Client Libraries and Code Generation Tool (just the binaries, and a new DataSvcUtil tool)
  • OData Client Sample for Windows Phone 7 (great for learning)

After downloading, don’t forget to “unblock” the ZIP files, or Visual Studio will grouch at you.  Just right-click on the ZIP file and choose Properties from the menu.  If there is a button at the bottom labeled “Unblock”, click it.  That’s it. Once you get the Client Libraries and Code Generation Tool zip file unblocked, and unzipped, replace the assembly reference in your project from the old CTP version of the System.Data.Services.Client.dll to the new version supplied in this new download. You’ll also need to re-generate the client proxy in your code using the new version of DataSvcUtil.  Remember how?  Just run this command from a Command Prompt opened to the folder where you unzipped the tools DataSvcUtil /uri:http://odata.netflix.com/catalog /dataservicecollection /version:2.0 /out:NetflixCatalog.cs Note: You must include the /dataservicecollection attribute (which itself requires the /version:2.0 attribute) to get INotifyPropertyChanged attached to each of your generated entities.  If you’re not going to use the DataServiceCollection objects as I will, then you might not need this, but I’m using it in my samples if you’re following along at home. Now you can replace your old proxy classes with these new ones.  Now is when the real fun begins…

Wherefore art thou LINQ?

The most impactful change has got to be the removal of LINQ support by the DataServiceProvider.  From Mike’s post:

LINQ support in the client library has been removed as the core support is not yet available on the phone platform.  That said, we are actively working to enable this in a future release.  Given this, the way to formulate queries is via URIs.

Wow.  I don’t know about you, but I have come to depend on LINQ for almost everything I do in .NET anymore, and this one really hits me square between the eyes. Fortunately, these URI-based queries aren’t too complicated to create, and Mike also points out that they’ve added a new method on the context to make this a bit easier on us:

We’ve added a LoadAsync(Uri) method to the DataServiceCollection class to make it simple to query services via URI.

With my custom URI and this new method, it’s actually almost as simple as before, sans LINQ doing all my heavy lifting.

Surgery time

So – to finish upgrading my Netflix application, I’ve got to make some changes to the existing MainViewModel.LoadRuntimeData method.  Here’s what it looks like from the last implementation:

private void LoadRuntimeData()
{
    Uri serviceUri = new Uri("http://odata.netflix.com/catalog", UriKind.Absolute);
    NetflixCatalog _catalog = new NetflixCatalog(serviceUri);
    var query = _catalog.Titles
        .Where(t => t.Rating == "PG" && ReleaseYear = 1983)
        .OrderByDescending(t => t.AverageRating)
        .Take(20);
    var dsq = query as DataServiceQuery<Title>;
    dsq.BeginExecute(
        (callback) =>
        {
            var results = dsq.EndExecute(callback);
            DispatcherHelper.CheckBeginInvokeOnUI(() =>
            {
                Items.Clear();
                foreach (var item in results)
                {
                    Items.Add(item);
                }
            });
        }, null);
}

As you can see, this contains quite a bit of LINQ magic. Unfortunately, that's going to have to go. The only thing we can really salvage, is the instantiation of the NetflixCatalog class based on the Uri to the main OData service endpoint.  This is important – we don’t use a URI with all the query string parameters in one shot because the DataContext class needs to have a reference to the base URI for the service, and future queries will be based on that base URI.

Rebuild the query

Now that we have a reference to the main service, we now have to build our own query. To do this, we’ll need to manually convert the LINQ query into something that the previous DataContext would have done for us.  There are a couple ways to figure this out. First, we could dive into the OData URI Conventions page on the OData web site and read all about how to use the various $ parameters on the URL string to get the results we want.  The sneaky way, however, would be to run the old code with the old provider and look at the URI that got sent to the Netflix service by snooping on it with Fiddler.   The results are the same – one long URL that has all the parameters included.

http://odata.netflix.com/Catalog/Titles?$filter=Rating eq 'PG' and ReleaseYear eq 1983&$orderby=AverageRating desc&$top=20

Although it's pretty long, note how clean the URL is – Where becomes $filter, OrderBy becomes $orderby and Take becomes $top. This is what is so great about OData: clean, clear, easy to read URLs. Armed with our new URI, we can go in and replace the LINQ query with a new Uri declaration. Since we already have the new DataContext based on the service’s URI, we can remove that from here and just use the stuff that comes after:

/Titles?$filter=Rating eq 'PG' and ReleaseYear eq 1983&$orderby=AverageRating desc&$top=20

Don’t forget to mark this new URI as UriKind.Relative.  This new URI is definitely NOT an absolute URI, and you’ll get an error if you forget. Here’s what the new code looks like so far:

Uri serviceUri = new Uri("http://odata.netflix.com/catalog", UriKind.Absolute);
NetflixCatalog catalog = new NetflixCatalog(serviceUri);

Uri queryUri = new Uri("/Titles?$filter=Rating eq 'PG' and ReleaseYear eq 1983&$orderby=AverageRating desc", UriKind.Relative);

ObservableCollection –> DataServiceCollection

Now that the DataContext is created, and the replacement query is built, it's time to load up the data. But what are we going to load it up into? Depending on which version of my old application you were using, you might see code like I listed above with ObservableCollection or you might have the version that I converted to use the DataServiceCollection.  This new model definitely wants us to use DataServiceCollection as it has some neat ways to manage loading data for us.  That means we will have to swap out our definition of the Items property with a DataServiceCollection. First, replace instances of ObservableCollection with DataServiceCollection.  Second, remove the initializer for the Items property variable – it’s no longer needed. Third, and this is optional, you can tap into the Paging aspects of the DataServiceCollection by adding a handler to the DataServiceCollection.Loaded event. Note: I don’t need this feature now, so I’m not going to add code for it.  I’ll leave it as an exercise for the reader,or you can hang on for a future post where I add this back in.

Run the query

Now that my query URIs are defined, and my DataServiceCollection objects are in place, it’s time to wire up the final changes to the new query.  For this, all I have to do is initialize the Items property with a DataServiceCollection and ask it to go run the query for us.

_items = new DataServiceCollection<Title>(catalog);
_items.LoadAsync(queryUri);

Notice the simplified version of the loading process.  Instead of having to go through and manually load up all the items in the ObservableCollection, here the DataServiceQuery handles all that hard work for us.  The main thing we need to remember is to initialize it with the DataContext before calling out to it.

Wrapping it all up

Now that we’ve got everything working, let’s take a look at the whole LoadRuntimeData method:

private void LoadRuntimeData()
{
    Uri serviceUri = new Uri("http://odata.netflix.com/catalog", UriKind.Absolute);
    NetflixCatalog catalog = new NetflixCatalog(serviceUri);

    Uri queryUri = new Uri("/Titles?$filter=Rating eq 'PG' and ReleaseYear eq 1983&$orderby=AverageRating desc&$top=20", UriKind.Relative);

    _items = new DataServiceCollection<Title>(catalog);
    _items.LoadAsync(queryUri);
}

Except for a few minor changes to the ViewModel properties (all we really did was change a type from ObservableCollection to DataServiceQuery) the actual code changes were pretty minimal. I still don’t like that I have to write my own URL string, but the team is going to address that for me in the future, so I guess I can hang on until then. I’ve uploaded the project to my Skydrive, so you can download this version to see it in action.  It’s still not a very exciting application, but it does show off how to use the new OData library.  As always, thanks for reading, I hope you found it valuable, and let me know if you have any questions.

Comments (25)

 

  1. Chris Woodruff says:

    To work with the new OData Client Library with developing Windows phone 7 applications there is a hint and 2 gotcha’s:

    Hint — If you are a LINQ’er and want to get your URI’s written quickly go to my blog post “Examining OData – Windows Phone 7 Development — How to create your URI’s easily with LINQPad” at http://www.chriswoodruff.com/index.php/2010/10/28/examining-odata-how-to-create-your-uris-easily-with-linqpad/

    Gotcha — Seems the ability to use the Reactive Framework has been broken with this new update of the OData Client Library. To do Async loads of data calls on the DataServiceCollection, you need to have an observer tied to your class like most MVVM patterns show. Rx is different as it allows Observers to subscribe to your events which does not seem to be handled in the code for the DataServiceCollection written for WP7.

    One more Gotcha — If you have the new Visual Studio Async CTP installed on your machine you will not be able to compile the source of the OData Client Library that is on the CodePlex site. It has variable names of async that conflict with the new C#/VB.NET keywords async and await.

  2. jatinam says:

    Thanks for the article. A couple of comments though. Most of the articles I have read don’t make it very clear how to generate the client proxy. For example you mentioned above
    “You’ll also need to re-generate the client proxy in your code using the new version of DataSvcUtil. Remember how? Just run this command from a Command Prompt opened to the folder where you unzipped the tools
    DataSvcUtil /uri:http://odata.netflix.com/catalog /dataservicecollection /version:2.0 /out:NetflixCatalog.cs”
    This is completely confusing to a person who is trying to learn WP7. First of all, instead of “Command Prompt”, it should be “Open a VISUAL STUDIO COMMAND PROMPT”. It will work from the Command Prompt only if you have the VS environment variables set, which spme people may not have. So, instructions such as “Open VS 2010 Command Prompt”, instead of “Command Prompt” saves the user some frustration.
    Second, “Just run this command from a Command Prompt opened to the folder where you unzipped the tools” is completely misleading. One doe not need to run this in the folder where you unzipped the tools. After you open the VS 2010 command prompt, you can just run “DataSvcUtil /uri:http://odata.netflix.com/catalog /dataservicecollection /version:2.0 /out:NetflixCatalog.cs” in any folder, like c:test or where your VS solution or project lives. The idea is to generate a Netflixcatalog.cs file with this command and include in your VS project, you do not have to run this command in the folder where you unzipped the tools. Is my understanding correct?
    OData client library for Windows Phone 7 is pretty new and was with some bugs before and now he LINQ support is removed with this new version, will be included in the next version, all these kind of things have led to confusions.
    That said, WP7 is absolutely amazing, it’s just that the team at Redmond needs to do a little bit of streamlining for developers to understand the tools and technologies.

    • Chris Koenig says:

      Sorry for not being clear enough. I guess I had in my mind that people were already experienced with the DataSvcUtil tool.

      To your question about DataSvcUtil – the OData tool update released a new version of the DataSvcUtil.exe utility, so if you don’t use the one that comes with the update (as opposed to the one that ships with .NET) you might get unexpected results. I would *definitely* recommend using the version of DataSvcUtil that comes with the updated OData tools for any WP7 projects using OData.

      And LINQ support is on the way, it’s just not part of this release.

      Cheers!
      Chris

  3. jatinam says:

    By the way, sorry about the comment “ThantTha ” below. The page behaves totally weird in IE9 Beta when writing comments. When I start typing into the comments box, the words are invisible, then when you presee “Backspace”, the words start appearing, and then you start typing the words disappear again..totally weird, that caused me to post a comment without actually knowing that there was something to post or not.

    • Chris Koenig says:

      Yeah, sorry about that. I use Disqus as my comment engine, and apparantly they’re not up to speed with IE9 beta yet. Hopefully they’ll catch up soon as many of my readers are now using IE9…

      Thanks!
      Chris

  4. jatinam says:

    sorry. I have to apologize. Totally did not understand the post initially. As the post said..
    “You’ll also need to re-generate the client proxy in your code using the new version of DataSvcUtil. Remember how? Just run this command from a Command Prompt opened to the folder where you unzipped the tools”, it’s the
    “OData Client Libraries and Code Generation Tool (just the binaries, and a new DataSvcUtil tool)”
    As there are 3 things to be downloaded, I was totally confused as to what apples to which download. Your post is totally correct. It’s just the plethora of downloads that were confusing me.
    Thanks for the post.

  5. Mike Flasko says:

    Great write up Chris! I’m sure others migrating applications will benefit from this.

    -Mike Flasko

  6. Matthias says:

    Hi!

    Is there a possibility to query the OData service from this WP7 client lib release using JSON as transport format?

    Thanks, Matthias

    • Chris Koenig says:

      Best I can tell it does not. You can still query JSON data the old fashioned way (HttpWebReqeust + DataContractJsonSerializer) but I couldn’t find a way to use the OData library to do this for you automatically when you query using the DataServiceCollection.LoadAsync method. I’ll poke around and see what I can learn, but it does look like that functionality is not currently included.

      Cheers!
      Chris

  7. I can’t use LINQ only in my WP7 apps? But when building apps in .NET on the desktop or SL I still can use LINQ or not?

    • Chris Koenig says:

      You can ABSOLUTELY use LINQ in Windows Phone 7 – I use it all the time. In fact, I use it in the sample you’re referencing. However – the latest OData client does NOT support the execution of LINQ queries against the OData data source. For that, you have to generate your own URL-based query. I recommend downloading LINQPad (http://linqpad.com) to help generate that query and make it easier until the SDK is updated with direct support for LINQ.

      Cheers!
      Chris

      Chris Koenig | +1 (214) 385-5616 | Developer Evangelist
      blog: http://chriskoenig.net/ | twitter: @chriskoenig

      • I know I can use LINQ in WP7 what I meant was that you can’t use LINQ (lets say) to OData in WP7 in the current client libraries. Correct? :)

        • Chris Koenig says:

          Correct – you have to take what the LINQ processor would have generated, and turn it into a query string. The team is working on re-integrating LINQ into a future release of the toolkit. For now, we use LINQPad or just our own noodle to generate our own

          Cheers!
          Chris

  8. Wma4432 says:

    Will the following code run in silverlight using WCF DataServices?

    private void LoadRuntimeData()
    {
    Uri serviceUri = new Uri(“http://odata.netflix.com/catalog”, UriKind.Absolute);
    NetflixCatalog catalog = new NetflixCatalog(serviceUri);

    Uri queryUri = new Uri(“/Titles?$filter=Rating eq ‘PG’ and ReleaseYear eq 1983&$orderby=AverageRating desc&$top=20″, UriKind.Relative);
    _items = new DataServiceCollection(catalog);
    _items.LoadAsync(queryUri);

    It complians that the queryUri in not the correct type? Can we execute a query where the format of the uri is as listed above? in Silverlight?

    Thank you,
    William Apken

    • Chris Koenig says:

      One minor change required:

      _items = new DataServiceCollection(catalog);

      DataServiceCollection requires the generic parameter for the items it will be holding.

  9. Xamlgeek says:

    Thanks! that was just the input I needed!

  10. BTW: I just posted this project which is a simple Fluent LINQ style library for generating OData query fragments.
    http://code.msdn.microsoft.com/oquery

    Enjoy!
    -Tom

    • Chris Koenig says:

      Thanks for your email! I am currently out of the office, returning Monday, February 7th. Until that time I will have limited access to email, so I will respond as quickly as I can. If your need is urgent, please call my cell phone at +1-214-385-5616.

      Cheers!
      Chris

      Chris Koenig | +1 (214) 385-5616 | Developer Evangelist
      blog: http://chriskoenig.net/ | twitter: @chriskoenig

  11. [...] Browser for Windows Phone 7 – Part 1, Part 2 OData v2 and Windows Phone 7 Data Services (OData) Client for Windows PHone 7 and LINQ Learning OData? MSDN and I Have the [...]

  12. [...] Browser for Windows Phone 7 – Part 1, Part 2 OData v2 and Windows Phone 7 Data Services (OData) Client for Windows PHone 7 and LINQ Learning OData? MSDN and I Have the [...]

  13. [...] Browser for Windows Phone 7 – Part 1, Part 2 OData v2 and Windows Phone 7 Data Services (OData) Client for Windows PHone 7 and LINQ Learning OData? MSDN and I Have the [...]

  14. [i][u][b]ugg sale [/b][/u][/i] School superintendent Dr Some of these sequences feature visually interesting scenes which are fed to the vision algorithmWebtrends / Google Analytics This is used to help us identify unique visitors to our websites National Football league is a great platform for the players of football teams, and a great source of entertainment for all the people of the world “I attempt to obtain mine as tight as you possibly can since they hold so negative At dinner time, we talked so many interesting things happen in our life, we all the college students and mutual ask how to live in the presentNelson told the trooper he had a pocket knife, and the trooper said that was fine Combine candy cane chunks with chocolate (add peppermint flavoring at this point if desiredIRS app gets updateAn IRS smartphone app which helps users with tax questions is now available in SpanishHis Sky teammates Bradley Wiggins and Chris Froome won gold and bronze respectively for Great Britain, while German Tony Martin was secondThe county is currently facing a $310 million budget deficit, and County Executive Ed Mangano says the change would save $20 Rep