In this recipe we will discuss how to implement create, update, and delete operations, also known as CRUD operations using DataServiceCollection
and Context
class. Here are the mappings for HTTP Verb to OData operation:
HTTP |
OData |
---|---|
GET |
Read |
POST |
Update |
PUT |
Insert |
DELETE |
Delete |
For this sample, we will be using the editable OData API exposed by odata.org
. Just a note, at the time of writing the book this service was provided for testing OData edit features with some restrictions.
You can browse OData using http://services.odata.org/(S(bltvbobia1rthiavqczdcr1u))/OData/OData.svc/. We will be using this link to consume the data in this recipe. For simplicity, we shall only update the categories.
To consume sample editable OData, we first need to generate the proxy class using the DataSvcUtil
using the following command-line:
datasvcutil /uri:http://services.odata.org/(S(bltvbobia1rthiavqczdcr1u))/OData/OData.svc/ /out:.EditODataModel.cs /Version:2.0 /DataServiceCollection
Ch4_Recipes
and add a new Phone Project with a name Recipe4_ODataCRUD
. EditODataModel.cs
we generated into the project by right-clicking and selecting Add Existing Item. You can also use Windows Explorer to just copy and paste the file into the project. System.Data.Services.Client
that comes with the SDK to the project. MainPage.xaml
file and let's add a ListBox
control with two text blocks, one for Category ID and another for Category Name. Add the Binding
property to both of them. Also, add the SelectionChanged
event for the ListBox
. Right-click on OnSelect and click on Navigate to Event Handler to create the empty OnSelect
event method:<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="Ch4 Recipes" Style="{StaticResource PhoneTextNormalStyle}"/> <TextBlock x:Name="PageTitle" Text="OData CRUD" Margin="9,- 7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> </StackPanel> <!--ContentPanel - place additional content here--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <ListBox x:Name ="lstResults" Grid.Row="3" Grid.Column ="1" ItemsSource="{Binding}" SelectionChanged="OnSelect"> <ListBox.ItemTemplate> <DataTemplate> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="20"/> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding ID}" FontWeight="Bold" Foreground="OrangeRed"/> <TextBlock Grid.Row="0" Grid.Column="1" TextWrapping="Wrap" Text="{Binding Name}" FontWeight="Bold" FontStretch="Expanded" Foreground="OrangeRed"/> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Grid>
Images
. Now right-click on the Images
folder and select Add Existing Items. Navigate to your local drive Program FilesMicrosoft SDKs
as shown in the following screenshot and select appbar.new.rest.png
. ApplicationBar
at the bottom to display one icon to add a new Category
. Also, add the click event to map to the ButtonAdd_Click
method:<phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar BackgroundColor="Orange" IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton IconUri="/Images/appbar.new.rest.png" Click="ButtonAdd_Click" Text="Add Task"/> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar>
MainPage.xaml.cs
file and include the following using
declarations at the beginning of the page:using ODataDemo; using System.Data.Services.Client; using System.Windows.Controls.Primitives;
MainPage
constructor:private Uri svcUri; private ODataDemo.DemoService ctx; private DataServiceCollection<ODataDemo.Category> rslts;
MainPage
method. Here we are creating the instance of the Uri
and calling the DemoService
. Similar to the last recipe, we will save the Category
collections in the DataServiceCollection
and call LoadAsync:
public MainPage_Loaded(object sender, RoutedEventArgs e) { svcUri = new Uri(@"http://services.odata.org/ (S(bltvbobia1rthiavqczdcr1u))/OData/OData.svc/"); var query = "Categories"; // create context ctx = new ODataDemo.DemoService(svcUri); rslts = new DataServiceCollection<ODataDemo.Category>(ctx); rslts.LoadCompleted += new EventHandler <LoadCompletedEventArgs>(rslts_LoadCompleted); rslts.LoadAsync(new Uri(query, UriKind.Relative)); }
rslts_LoadCompleted
. Here we check for any errors, otherwise we assign the query results to the DataContext:
private void rslts_LoadCompleted(object sender, LoadCompletedEventArgs e) { if (e.Error == null) { this.DataContext = rslts; } else { MessageBox.Show(string.Format("Error: {0}", e.Error.Message)); } }
OnSelect
event method. Here we get the SelectedIndex
and pass it as a query string. This is used for selecting:private void OnSelect(object sender, SelectionChangedEventArgs e) { var selector = (Selector)sender; if (selector.SelectedIndex == -1) return; this.NavigationService.Navigate(new Uri("/CategoryForm.xaml?selIndex = " + selector.SelectedIndex, UriKind.Relative)); selector.SelectedIndex = -1; }
CategoryForm.xaml
with query string selIndex
as zero. The reason for zero is to indicate that it is a new category:private void ButtonAdd_Click(object sender, EventArgs e) { this.NavigationService.Navigate(new Uri("/CategoryForm.xaml?selIndex = 0", UriKind.Relative)); }
CategoryForm.xaml
to the project. As this is for updating the Category
collection, let's add two textboxes and a save button, which would look like the following code snippet:<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="Edit OData App" Style="{StaticResource PhoneTextNormalStyle}"/> <TextBlock x:Name="PageTitle" Text="Category" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> </StackPanel> <!--ContentPanel - place additional content here--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <TextBlock Height="30" HorizontalAlignment="Left" Margin="28,43,0,0" Name="textBlock1" Text="ID" VerticalAlignment="Top" Width="115" /> <TextBox Height="72" HorizontalAlignment="Left" Margin="12,68,0,0" Name="txtID" Text="" VerticalAlignment="Top" Width="438" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="28,146,0,0" Name="textBlock2" Text="Name" VerticalAlignment="Top" Width="115" /> <TextBox Height="72" HorizontalAlignment="Left" Margin="12,171,0,0" Name="txtName" Text="" VerticalAlignment="Top" Width="438" /> <Button Content="Save" Height="72" HorizontalAlignment="Left" Margin="12,249,0,0" Name="button2" VerticalAlignment="Top" Width="136" /> </Grid> </Grid>
CategoryForm.xaml.cs
file and add a using
declaration at the top of the page for ODataDemo:
using ODataDemo;
MainPage
so it is added to the DataServiceCollection
. We can achieve this by either serializing the object to local storage, or just holding a global static variable. Open the App.xaml.cs
file and add static variable _category:
public static ODataDemo.Category _category;
ID
and Name
to the global variable and then navigate back to the previous page:private void btnSave_Click(object sender, RoutedEventArgs e) { App._category = new Category(); if (txtID.Text != "") App._category.ID = int.Parse(txtID.Text); if (txtName.Text != "") App._category.Name = txtName.Text; NavigationService.GoBack(); }
MainPage.xaml.cs
file and add the OnNavigatedTo
method. In this method, we check if there is anything in the static variable and then add the category object to the collection.// called when the current frame becomes active protected override void OnNavigatedTo(System.Windows.Navigation. NavigationEventArgs e) { if (App._category != null) { rslts.Add(App._category); } base.OnNavigatedTo(e); }
CategoryForm.xaml.cs
and add the OnNavigatedTo
method here too. Before the method, declare the string variable selIndex
. We will get the query string selIndex
using the NavigationContext
class. We will use this variable in the page-loaded event method to get the collection item.private string selIndex; protected override void OnNavigatedTo (System.Windows.Navigation.NavigationEventArgs e) { base.OnNavigatedTo(e); if (!NavigationContext.QueryString.TryGetValue("selIndex", out selIndex)) { MessageBox.Show("Error"); } } private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) { if (int.Parse(selIndex) != 0) { txtID.Text = App._category.ID.ToString(); txtName.Text = App._category.Name.ToString(); } }
CategoryForm
to edit and save. You can also click on the + button in the navigation bar to save a new category.First, we displayed the list of categories available in the OData list. By getting the list using DataServiceCollection
and context classes, we are making use of the automatic tracking for changes to the list.
By updating the object it is marked as changed. OData automatically updates the OData data source. We can add to and update the collection using DataServiceCollection's
add and update methods. Just like adding to and updating the collection, we can also remove from the collection.
You can easily navigate the structure of OData using free tools available online.
There are many different explorers available online for browsing the OData:
http://www.silverlight.net/content/samples/odataexplorer/default.html
Check Chapter 6 for REST web services and Chapter 7 for WCF web services.
Also, check the following online resources for further understanding of OData:
http://msdn.microsoft.com/en-us/library/hh394007(v=VS.92).aspx
http://samidipbasu.com/2011/07/24/updating-odata-data-source-from-wp7-part-1/