Working with Data
Our mobile client will be able to perform classic operations such as reading, deleting, and inserting data that will be saved back to the original LightSwitch data source via the OData service. The first task is to load data for reading. Listing 5 demonstrates how to create a couple of methods that load customers, orders, and the event handlers for the DataServiceCollection.LoadCompleted event.
Listing 5Loading data asynchronously.
'Querying the data service for the full list of items 'and loading data asynchronously Private Sub loadCustomers() Dim query = From cust In Me.applicationData.Customers Order By cust.CompanyName Descending Select cust customers.LoadAsync(query) End Sub Private Sub loadOrders() orders.LoadAsync(New Uri("http://localhost/ExposingOData/ApplicationData.svc/Orders", UriKind.Absolute)) End Sub Private Sub orders_LoadCompleted(sender As Object, e As LoadCompletedEventArgs) 'If the feed exposes paged data, load the next chunk of data If e.Error Is Nothing Then If Me.orders.Continuation IsNot Nothing Then Me.orders.LoadNextPartialSetAsync() Else 'Otherwise simply assign the data collection as the 'data source of the ContentPanel grid Me.ContentPanel.DataContext = Me.orders End If Else MessageBox.Show(e.Error.Message) End If End Sub Private Sub customers_LoadCompleted(sender As Object, e As LoadCompletedEventArgs) If e.Error Is Nothing Then 'No assignment of the customers collection 'because it will not be displayed in the UI If Me.customers.Continuation IsNot Nothing Then Me.customers.LoadNextPartialSetAsync() End If Else MessageBox.Show(e.Error.Message) End If End Sub
The DataServiceCollection class exposes an instance method called LoadAsync that loads data asynchronously, meaning that the user interface remains responsive (which is very important in Windows Phone apps). LoadAsync can receive as an argument either the result of a Language Integrated Query (LINQ), and is therefore of type IQueryable(Of T), or the URI of the entity set. The first approach is useful if you want to query an entity set with filters or sorting conditions; the second is preferable when you want to load (or reload) data from the actual data source, instead of using an in-memory collection.
Once the load operation completes, the runtime raises a LoadCompleted event. It might happen that an OData feed exposes paged data. In this particular case, we invoke the LoadNextPartialSetAsync method. However, if data has been fully loaded, the data is assigned to the ContentPanel object as the data source for presentation controls.
The next step is adding code for deleting and adding orders. Deleting a selected order is very simple, because you just need to retrieve the current object from the ListBox control, convert it to the appropriate type, and then mark it for deletion. Inserting an order is also easy, but it would require designing a specific page where the user can enter custom information. For this reason (and for the sake of keeping this article's examples relatively simple), the code presented here creates a new order, programmatically supplying hard-coded values at runtime. Listing 6 shows the code for deleting and adding orders.
Listing 6Deleting and inserting order instances.
'Delete the selected order 'If you are passing credentials of a user that is not authorized 'the code throws a NotAuthorizedException Private Sub DeleteButton_Click(sender As System.Object, e As System.EventArgs) Dim currentOrder = TryCast(Me.OrdersListBox.SelectedItem, OrdersServiceReference.Order) If currentOrder IsNot Nothing Then Me.orders.Remove(currentOrder) End If End Sub Private Sub InsertButton_Click(sender As System.Object, e As System.EventArgs) 'Hard coding the creation of a new order 'Assuming there is already a customer with the specified name Dim customerInstance = customers.Where(Function(c) c.CompanyName.ToLower.Contains("del sole")).tOrDefault Dim newOrder As New OrdersServiceReference.Order With newOrder .OrderDate = Date.Today .Description = "Order of Natural Water" .ShippedDate = Date.Today.AddDays(1) .RequiredDate = Date.Today.AddDays(2) .Customer = customerInstance End With Me.orders.Add(newOrder) End Sub
You delete and add items directly over the DataServiceCollection(Of T), exactly as you would do in any other collections, by using methods like Remove and Add. If you wanted to update an object, you could first get the instance of that object and then edit its properties. Actually, the ApplicationData class (and more generally proxy classes representing data sources) also exposes the DeleteObject, AddObject, and UpdateObject methods. These methods require the instance of the object that you want to delete, add, or edit. They work against the data service, not with the DataServiceCollection, so in the case of data-binding it wouldn't be a good choice, because bindings wouldn't update automatically.
Whatever your choice is, invoking such methods doesn't also save changes back to the data source; instead, they just keep track of changes in memory until a save operation is invoked. In our specific case for this article, entities are removed from and added to an in-memory collection, but changes are not sent to the data service until you invoke a save operation. Saving data is also an asynchronous operation. Since we provided a Save button, let's handle the Click event for that button now, as demonstrated in Listing 7.
Listing 7Saving changes through the OData service.
Private Sub SaveButton_Click(sender As System.Object, e As System.EventArgs) 'Start saving asynchronously Me.applicationData.BeginSaveChanges(SaveChangesOptions.Batch, AddressOf OnChangesSaved, Me.applicationData) End Sub Private Sub OnChangesSaved(result As IAsyncResult) ''Use the dispatcher to run the operation inside the appropriate thread Dispatcher.BeginInvoke(Sub() 'Get the instance of the application data Me.applicationData = CType(result.AsyncState, ApplicationData) Try 'Finalize the save operation Me.applicationData.EndSaveChanges(result) 'Reload data for data-binding loadOrders() Catch ex As DataServiceRequestException MessageBox.Show(String.Format("Data service error: {0}", ex.Message)) Catch ex As Exception MessageBox.Show(String.Format("Error: {0}", ex.Message)) End Try End Sub) End Sub
You invoke BeginSaveChanges to start an asynchronous operation. When the operation is completed, a callback is invoked; here you'll finalize the save operation by invoking EndSaveChanges, and reload data for data-binding.