Home > Articles > Mobile Application Development & Programming

  • Print
  • + Share This
From the author of Connecting to the Service

Connecting to the Service

Except for a couple of platform-specific edits, the code that you'll have to write to interact with the OData service is the same as in Part 3. In the code-behind file for the main page, write the code shown in Listing 4, which is responsible for establishing a connection to the service passing credentials and for keeping in memory the information retrieved from the service.

Listing 4—Connecting to the OData service and declaring class-level variables.

'The following directives are required
'Imports System.Data.Services.Client
'Imports MobileOrdersClient.OrdersServiceReference

'Store the service URL. Replace localhost with your server's name
Private Shared ReadOnly ServiceUri As _
        New Uri("http://192.168.1.3/ExposingOData/ApplicationData.svc")

'Two collections for storing the list of entities exposed by the service
Private Property orders As DataServiceCollection(Of Order)
Private customers As DataServiceCollection(Of Customer)

'Represent the LightSwitch's intrinsic data
Private applicationData As OrdersServiceReference.ApplicationData

Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
    If Me.applicationData Is Nothing Then
        Me.applicationData = New OrdersServiceReference.ApplicationData(ServiceUri)
    End If

    'Pass credentials of a LightSwitch user
    Me.applicationData.Credentials = New NetworkCredential("TestUser", "TestUser$")

    'Create instances of collections to store data
    Me.orders = New DataServiceCollection(Of Order)(applicationData)
    Me.customers = New DataServiceCollection(Of Customer)(applicationData)

    'Declare event handlers for handling loaded data
    AddHandler Me.orders.LoadCompleted, AddressOf orders_LoadCompleted
    AddHandler Me.customers.LoadCompleted, AddressOf customers_LoadCompleted

    'Load customers and orders
    loadCustomers()
    loadOrders()
End Sub

The code still uses the DataServiceCollection(Of T) class, which allows for storing collections of objects exposed by a WCF Data Service. As a reminder, this class inherits from ObservableCollection(Of T), which means that it implements the necessary infrastructure to send and receive notifications of changes to the items it contains. The first difference from other client types is that in WinRT you don't place the code that creates an instance of the service inside the constructor; instead, you place this code in the OnNavigatedTo event handler, which is raised when the user navigates to this page. Of course, a check is required to ensure that an instance of the service isn't already alive. The code accomplishes this with the first If block in the event handler, which avoids generating a second instance if one already exists.

The next step is writing code that loads data, shown with comments in Listing 5.

Listing 5—Loading 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

'Alternative syntax using the service URL
Private Sub loadOrders()
    orders.LoadAsync(New Uri("http://localhost/ExposingOData/ApplicationData.svc/Orders",
                             UriKind.Absolute))
End Sub

Private Async 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
        Dim dialog As New Windows.UI.Popups.MessageDialog(e.Error.Message)
        Await dialog.ShowAsync
    End If
End Sub

Private Async 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
        Dim dialog As New Windows.UI.Popups.MessageDialog(e.Error.Message)
        Await dialog.ShowAsync
    End If
End Sub

A few more considerations are required when talking about WinRT and Windows 8. As for Windows Phone, in Windows 8 Store Apps every operation must be asynchronous, meaning that the user interface must never be blocked and must always remain responsive even in the case of long-running operations. The code that loads and queries data is already asynchronous, so the code written in Part 3 can be used here. However, in WinRT, message dialogs must be asynchronous as well. For this reason there's no MessageBox object, as you would have in any other .NET development platform. If you want to tell users that something went wrong, you have different alternatives, such as toast notifications or the Windows.UI.Popups.MessageDialog object, which is easier to implement. This is an asynchronous object, so you must invoke its ShowAsync method, together with the Await operator. Also, the method that calls an asynchronous operation must be decorated with the Async modifier, as required by the Asynchronous Programming Pattern.

The final step is writing code that executes some operations over data, such as adding, removing, and saving orders. Listing 6 shows this code.

Listing 6—Executing data operations.

Private Sub InsertButton_Click_1(sender As Object, e As RoutedEventArgs)
    '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")).FirstOrDefault

    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

Private Sub DeleteButton_Click_1(sender As Object, e As RoutedEventArgs)
    Dim currentOrder = TryCast(Me.OrdersListView.SelectedItem, OrdersServiceReference.Order)

    If currentOrder IsNot Nothing Then
        Me.orders.Remove(currentOrder)
    End If
End Sub

Private Sub SaveButton_Click_1(sender As Object, e As RoutedEventArgs)
    'Start saving asynchronously
    Me.applicationData.BeginSaveChanges(SaveChangesOptions.Batch,
     AddressOf OnChangesSaved, Me.applicationData)
End Sub

Private Async Function OnChangesSaved(result As IAsyncResult) As Task
    ''Use the dispatcher to run the operation inside the appropriate thread
    'Get the instance of the application data
    Me.applicationData = CType(result.AsyncState, ApplicationData)

    Dim errorMessage As String = ""
    Try
        'Finalize the save operation
        Me.applicationData.EndSaveChanges(result)
        'Reload data for data-binding
        loadOrders()
    Catch ex As DataServiceRequestException
        errorMessage = String.Format("Data service error: {0}", ex.Message)
    Catch ex As Exception
        errorMessage = String.Format("Error: {0}", ex.Message)
    End Try

    If errorMessage <> "" Then
        Dim dialog As New Windows.UI.Popups.MessageDialog(errorMessage)
        Await dialog.ShowAsync
    End If
End Function

The code simulates an insert operation by hard-coding a new instance of an order, rather than providing the user with the ability to specify an order. This design is intended to make the example simpler, but of course in real-world applications you would provide a specific page with text boxes and other controls. About the save operation, notice how the code stores error message in a variable (if any) and based on the content of the variable it invokes the asynchronous message dialog. This technique is required since the Await operator cannot be used inside a Try..Catch block, so you need a way to call it from the outside. Apart from this, the code is the same as in Part 3.

  • + Share This
  • 🔖 Save To Your Account