Random geekery since 2005.

Husband, father, programmer, speaker, writer, blogger, podcaster, collector, traveler, golfer.

31 Days of Mango | Day #14: Using OData

Written in

by

Day14-ODataDay13-MarketplaceTestKitDay12-BetaTestingDay11-LiveTilesDay10-NetworkInformation

This article is Day #14 in a series called 31 Days of Mango, and was written by guest author Chris Woodruff. Chris can be reached on Twitter at @cwoodruff.  Chris is also a host of the podcast Deep Fried Bytes, so make to check that out as well.

Today, we will be looking at how to consume data from an OData feed and also perform full data duties that Windows Phone 7 apps need to perform.

What is OData?

The official statement for Open Data Protocol (OData) is that is a Web protocol for querying and updating data that provides a way to unlock your data and free it from silos that exist in applications today. Really what that means is that we can select, save, delete and update data from our applications just like we have been against SQL databases for years. The benefit is the ease of setting up and libraries that Microsoft has created for us the developers of Windows Phone 7 Mango apps. The benefit comes from the fact that OData has a standard that allows a clear understanding of the data due to the metadata from the feed.

Behind the scenes, we send OData requests to a web server that has the OData feed through HTTP calls using the protocol for OData. You can read more about OData here.

Setting up OData for your Mango Application

Now with the Windows Phone SDK 7.1, we have a great experience to use OData in our applications. In the previous version of the Windows Phone SDK 7.0, we had to use an external tool to generate our proxy classes based on a given OData feed. Now we have built in Visual Studio 2010 support. To import an OData feed first create a new Silverlight for Windows Phone/Windows Phone Databound Application project.

clip_image002

We will have created a project with the initial project set as shown below.

clip_image003

To import and get a your OData feed accessible from your project you will need to add the feed through the Add Service Reference in visual Studio 2010.

clip_image004

The Add Service Reference will be presented to you and you will enter the URL for the OData feed you wish to work with as shown below. Press the Go button for Visual Studio 2010 to gather the information about the feed and finally press OK to add the OData feed to your project.

We will use for this project an OData feed with historical baseball statistics.

http://www.baseball-stats.info/OData/baseballstats.svc/

clip_image006

You now have your proxy classes generated that will allow you to work with data from the OData feed you have set up within your project. By adding the OData feed to your project, you have also added the reference to the System.Data.Services.Client assembly that will allow you to work with the OData feed and data.

The DataServiceCollection class

The System.Data.Services.Client namespace is a rather magical set of classes that will allow you to do many exciting things with your OData feed. We will not go into this namespace other than the DataServiceCollection but I hope you explore this rich collection of classes at some point.

The DataServiceCollection class is the class that will give your project and you as a developer the opportunity to perform a full array of work using the data and metadata of your OData feed. You will have the full capabilities at your disposal based on the rules that were used to develop the OData feed. At best the OData will allow you to perform full CRUD (Create, Read, Update, Delete) on the data. In this article we will only be looking at the Read functionality of the DataServiceCollection to allow you to retrieve and bind data within your application.

Retrieving Data from an OData feed

We will dive right into the work and get started. You will have a MainPage.xaml file in your project that was created as the starting or home page for your application as shown below. I have updated the ApplicationTitle and PageTitle properties in the XAML to have a better application description.

clip_image008

Add the following using statements to your project. This will allow you to use the DataServiceCollection as well as the service reference we set up earlier.

using System.Data.Services.Client;
using _31DaysMangoOData.ServiceReference1;
 

Create the following private variables in the MainPage.xaml.cs code for the MainPage. This will give you a reference to your OData feed via the _context variable, the collection of Major League Baseball teams (_teams) that exists in the OData feed and finally the selected team (_selectedTeam) that will allow the project to show a list of the years the selected team has played.

private TeamFranchise _selectedTeam;
private BaseballStatsEntities _context;
 

We will need to get the data loaded from the OData feed and we will next implement the Loaded event for the Main Page as shown in Figure 6.

private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    _context = new BaseballStatsEntities(new Uri("http://www.baseball-stats.info/OData/baseballstats.svc/"));

    App.ViewModel.Teams.LoadCompleted += Teams_Loaded;

    var query = from t in _context.TeamFranchise
                orderby
                    t.franchName
                where t.active == "Y"
                select t;
    App.ViewModel.Teams.LoadAsync(query);
}
 

I get asked why we need to again give the URI for the OData feed. Did we not just give that when adding the Service Reference? We did but that reference to the URI of the OData feed was just to allow the generation of the proxy classes based on the metadata from the feed. We need to always give the URI to the OData feed service point when creating our data context.

We initialize the _teams private variable as a DataServiceCollection<T> generic of type TeamFranchise which is an entity type that exists in the OData feed. We associate the _teams’ LoadCompleted event handler with a new method we will look at soon names Teams_Loaded and finally we will get our data using LINQ. One of the great reasons to use the DataServiceCollection<T> class is that it allows you to create LINQ statements to query the data as well as bind data directly to our XAML views (we will look at that in the next section).

The final action we must put into place is to get the data set up in the LINQ statement. The DataServiceCollection class will asynchronously retrieve the data for us also. Remember that just like Silverlight, our Windows Phone 7 Mango projects must perform all actions asynchronously.

We need to have a method that will load the data into the DataContext we have created

private void Teams_Loaded(object sender, LoadCompletedEventArgs e)
{
    if (e.Error == null)
    {
        if (App.ViewModel.Teams.Continuation != null)
        {
            App.ViewModel.Teams.LoadNextPartialSetAsync();
        }
        else
        {
            // Set the data context of the list box control to the team data.
            this.LayoutRoot.DataContext = App.ViewModel.Teams;
        }
    }
    else
    {
        MessageBox.Show(string.Format("We have an error: {0}", e.Error.Message));
    }
}
 

We now have our baseball teams loaded in the _teams variable and ready to bind. Before we show how to bind data to the MainPage of the project, I want to look at how to also work with associated entity classes and data based on the _teams variable.

We need to modify the MainViewModel to contain the data from the OData feed that was queried in MainPage.xaml.cs. Here is the code for the MainViewModel.cs.

using System;
using System.ComponentModel;
using System.Data.Services.Client;
using _31DaysMangoOData.ServiceReference1;

namespace _31DaysMangoOData
{
    public class MainViewModel : INotifyPropertyChanged
    {
        public MainViewModel()
        {
            this.Teams = new DataServiceCollection<TeamFranchise>();
        }

        /// <summary>
        /// A collection for ItemViewModel objects.
        /// </summary>
        public DataServiceCollection<TeamFranchise> Teams { get; private set; }

        public bool IsDataLoaded
        {
            get;
            private set;
        }

        /// <summary>
        /// Creates and adds a few ItemViewModel objects into the Items collection.
        /// </summary>
        public void LoadData()
        {
            this.IsDataLoaded = true;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (null != handler)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
 

Binding Data to your Views from OData

Now we will dive into the XAML side of the project to show how the data from the OData feed that is located in the MainViewModel is bound the ListBox in the MainPage view and also to the DetailsPage view.

The MainPage.xaml has been associated with the MainViewModel located in the MainViewModel.cs file through the DataContext we set up in the MainPage’s constructor.

DataContext = App.ViewModel;
 

Not much has to be done to the XAML code that was created when we started the Windows Phone Databound Application project. We removed the following line since we do not have design-time data.

d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
 

We also need to change the Binding of the MainListBox to just {Binding} since we set the following line of code in the MainPage.xaml.cs Teams_loaded method.

this.LayoutRoot.DataContext = App.ViewModel.Teams;

This will have the DataServiceCollection containing the baseball teams we retrieved from the OData team ready to use and bind in the XAML of the MainPage.

The following is the XAML source for the MainPage.

<phone:PhoneApplicationPage
   x:Class="_31DaysMangoOData.MainPage"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
   xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
   xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008&quot;
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
   mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
   FontFamily="{StaticResource PhoneFontFamilyNormal}"
   FontSize="{StaticResource PhoneFontSizeNormal}"
   Foreground="{StaticResource PhoneForegroundBrush}"
   SupportedOrientations="Portrait" Orientation="Portrait"
   shell:SystemTray.IsVisible="True">

    <!–Data context is set to sample data above and LayoutRoot contains the root grid where all other page content is placed–>
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!–TitlePanel contains the name of the application and page title–>
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="31 Days of Mango" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="baseball teams" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!–ContentPanel contains ListBox and ListBox ItemTemplate. Place additional content here–>
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <ListBox x:Name="MainListBox" Margin="0,0,-12,0" ItemsSource="{Binding}" SelectionChanged="MainListBox_SelectionChanged">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                      <StackPanel Margin="0,0,0,17" Width="432" Height="78">
                          <TextBlock Text="{Binding Path=franchName}" TextWrapping="NoWrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                      </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Grid>

</phone:PhoneApplicationPage>
 

When we run the application in the Windows Phone Emulator you will see that the baseball teams now show in the application.

clip_image010

Getting the selected baseball team data to show after the application navigates to the DetailPage.xaml is very simple with a few lines of code. We want to assign the team that was selected in the MainPage and then associate the DataContext of the DetailsPage to the selected baseball team’s object in the DataServiceCollection located in the MainViewModel. The code for the DetailsPage.xaml.cs is below.

using System.Windows.Navigation;
using Microsoft.Phone.Controls;

namespace _31DaysMangoOData
{
    public partial class DetailsPage : PhoneApplicationPage
    {
        // Constructor
        public DetailsPage()
        {
            InitializeComponent();
        }

        // When page is navigated to set data context to selected item in list
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            string selectedIndex = "";
            if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
            {
                int index = int.Parse(selectedIndex);
                DataContext = App.ViewModel.Teams[index];
            }
        }
    }
}
 

Again the XAML code in DetailsPage.xaml is very simple also with just the ApplicationTitle being changed along with the PageTitle being data binded to the franchName property of the selected Team. I wanted to show the active property also just for additional information.

<phone:PhoneApplicationPage
   x:Class="_31DaysMangoOData.DetailsPage"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
   xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
   xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008&quot;
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
   mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
   FontFamily="{StaticResource PhoneFontFamilyNormal}"
   FontSize="{StaticResource PhoneFontSizeNormal}"
   Foreground="{StaticResource PhoneForegroundBrush}"
   SupportedOrientations="Portrait" Orientation="Portrait"
   shell:SystemTray.IsVisible="True">

    <!–Data context is set to sample data above and first item in sample data collection below and LayoutRoot contains the root grid where all other page content is placed–>
    <Grid x:Name="LayoutRoot" Background="Transparent" d:DataContext="{Binding Items[0]}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!–TitlePanel contains the name of the application and page title–>
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="31 Days of Mango" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="ListTitle" Text="{Binding franchName}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!–ContentPanel contains details text. Place additional content here–>
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <TextBlock x:Name="ContentText" Text="{Binding active}" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}"/>
        </Grid>
    </Grid>

</phone:PhoneApplicationPage>
 

Running the application and selecting a baseball team from the main page will then navigate you to the details page where you will see the following (love my Tigers!!).

clip_image012

Summary

To wrap up, using OData to consume data is very simple. There are many more features that the System.Data.Services.Client and specifically the DataServiceCollection can offer you as a developer. I hope you have seen how you can make your Windows Phone Applications data rich with very little effort.

To download this entire working project for Visual Studio 2010, click the Download Code button below.

download

Tomorrow, guest author Doug Mair will walk you through using the ProgressBar to both entertain and inform your user about a download or long-running process.  See you then!

toolsbutton

Tags

13 responses to “31 Days of Mango | Day #14: Using OData”

  1. Fast_Eddie Avatar
    Fast_Eddie

    Loved you final selection. My prediction is that this may be your last guest spot because Jeff is such an Indian’s fan :}

    I’d love to see a followup showing how to Update/Delete data using OData

    1. Chris Woodruff Avatar

      Why do you think I picked the Tigers!? 😉

      I will be doing a more detailed followup on my blog at some point about the other features of CRUD with OData. I will let Jeff know about it. This post would have become a tome if I detailed everything about the DataServiceCollection. If you have any questions just ask me through email or Twitter @cwoodruff

      Thanks for the comment.

  2. […] No trackbacks yet. « 31 Days of Mango | Day #14: Using OData […]

  3. […] 31 Days of Mango | Day #14: Using OData & Day #15: The Progress Bar – Jeff Blankenburg continues his series of 31 posts about Windows Mango with two guest posts, the first from Chris Woodruff on consuming OData in your applications, and the sceond on the use of the progress bar from Doug Mair. […]

  4. […] 31 Days of Mango | Day #14: Using OData […]

  5. David Stovell Avatar
    David Stovell

    Thanks… Great example.

    There is a bug however. The main page initialises the context object each time it loads. After you navigate to the details of a team and back to the main page a few times you start getting errors: “The context object is already tracking a different entity with the same resource uri.”

    You can fix this by creating the context in a static class (as per the MyVbProf.com approach).

    Create a class in a new class file (I called mine TeamFranchiseContext and put it in the ViewModels folder, but you can use your own name):
    =========================================
    public static class TeamFranchiseContext
    {
    private static BaseballStatsEntities _context;
    private static Uri serviceUri = new Uri(“http://www.baseball-stats.info/OData/baseballstats.svc/”);
    public static BaseballStatsEntities Context
    {
    get
    {
    if (_context == null)
    {
    _context = new BaseballStatsEntities(serviceUri);
    }
    return _context;
    }
    set { _context = value; }
    }
    }
    =========================================
    Note: you will need to add the following namespaces at the top of the class file:

    using System.Data.Services.Client;
    using _31DaysMangoOData.ServiceReference1;

    Then, in MainPage_Loaded you need to change this line:

    _context = new BaseballStatsEntities(new Uri(“http://www.baseball-stats.info/OData/baseballstats.svc/”));

    to this:

    _context = TeamFranchiseContext.Context;

    =====================================

  6. wloescher Avatar
    wloescher

    I missed the part where the ViewModels get created. Is that built in behavior for an MVC project? If so, how do I go about creating the necessary classes for a non-MVC project?

    1. wloescher Avatar
      wloescher

      nm…I figured it out by looking at the sample code. Excellent article…thanks!

  7. debrote Avatar

    I’m sure I am missing someghing but your framework for this project is Microsoft Entity Framework. Where can I get that? My environment only lists .Net Framework 2, 3, 3.5, and 4.

  8. Rainer Borchmann Avatar

    After a while clicking from Team to Team in der App comes an errormessage:

    ” We have an error: The context is already trackinga different entity with the same ressource Uri.”

    Ihave only one Projekt on the machin. exact this Day 14… what can i do?

    Please anser her or to rainer@borchmann.de

    Thanks

    Rainer

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at WordPress.com

%d bloggers like this: