This is the 9th article in our "Bring the clouds together: Azure + Bing Maps" series. You can find a preview of live demonstration on http://sqlazurebingmap.cloudapp.net/. For a list of articles in the series, please refer to http://blogs.msdn.com/b/windows-azure-support/archive/2010/08/11/bring-the-clouds-together-azure-bing-maps.aspx.

Introduction

The last post focused on the client side. We described how to display structured data with the help of jQuery Templates.

This post will combine the client with the cloud WCF Data Services we built earlier. After complete reading this post, you'll be able to consume an OData service (such as WCF Data Services) from an AJAX client.

OData clients overview

OData is an open protocol that is based on HTTP standard. So any clients that supports HTTP are able to consume the service. No matter what client library you're using, under the hook, you're always issuing HTTP requests like GET and POST. OData is used by a lot of Microsoft products, including Windows Azure Table Storage. OData supports common data formats such as ATOM and JSON. So you can use your existing knowledge to parse the data returned from the service (note certain services like table storage only supports a subset of the protocol).

Browser clients

The simplest way to access an OData service is to use your browser. In our sample, when testing against Development Fabric, you can open your browser and navigate to http://127.0.0.1:81/DataService/TravelDataService.svc/Travels. Then view source of the page. This will list all entities in the ATOM format.

You can also request a single entity by appending the key in the request. If your entity's key is a simple integer, you can use URI like http://host/yourservice.svc/YourEntitySet(21). This will return the entity whose ID is 21. In our sample, since the entity key contains two properties, we need to put both of them in the URI. For example: http://127.0.0.1:81/DataService/TravelDataService.svc/Travels(PartitionKey='testuser',RowKey=guid'783323a1-b1f1-4910-b5be-a2f37e62d0ba').

Additionally, you can perform common tasks such as filtering, sorting, and paging. We will not go into the details in this post. You can find the complete documentation on http://www.odata.org/developers/protocols/uri-conventions.

Fiddler client

Browser allows you to test GET operations using ATOM format. To use the JSON format, or to test other verbs like POST, you can take advantage of Fiddler. For example, to update an entity in our sample, you can use Fiddler's Request Builder feature to issue a PUT request. You can also specify the Content-Type as "application/json; charset=utf-8", so you can use the JSON format with UTF-8 encoding.

Fiddler Request Builder

Other clients

To consume OData services in your client application, you can either issue HTTP requests directly, or use a client library listed on http://www.odata.org/developers/odata-sdk. The client library for .NET/Silverlight is already built into .NET Framework/Silverlight SDK. The next section will focus on accessing OData services from AJAX clients. We assume most Azure developers already know how to use the .NET/Silverlight client library since this is the same library used to access table storage. So we'll briefly cover it in a future post when working with a Silverlight client.

Working with OData Services from AJAX clients

You can find an OData JavaScript client library on http://www.odata.org/developers/odata-sdk. Using this library is straightforward if you're working on ASP.NET AJAX. The syntax is exactly the same as ASP.NET AJAX. You can find a detailed tutorial on http://www.asp.net/ajaxLibrary/odata.ashx. So we will not repeat in this post.

Instead, we'll describe how to accessing OData services using jQuery. This approach requires slightly more code compared to using ASP.NET AJAX library. But it allows you to see the underlying HTTP requests. The basic idea is of course using the ajax function. We'll use the JSON format for all requests.

Query

To query a data service, you issue GET requests, as you do in the browser. For example, to query all entities created by a user (PartitionKey) ordered by time, you can use the following code:

    $.ajax(

        {

            type: 'GET',

            url: dataServiceUri + '/Travels?$filter=PartitionKey eq 'TestUser'&$orderby=Time',

            dataType: 'json',

            success: LoadDataCompleted,

            error: function () {

                $('#ErrorInforamtion').text('Error retrieving data. Please try again later.');

            }

        });

In the LoadDataCompleted callback function, you can parse the result JSON data, display them in the list, and add pushpins on the map. Please refer to our previous posts on how to do that.

Update

To update an entity, you issue a PUT request. You need to specify the keys in the URI, so the service is able to find the entity to update. You've already seen a sample of the request body in the above screenshot. Note the request body looks exactly like a JSON object. To construct the request body, you can either manually construct a string, or as recommended by the JSON group, use JSON stringify to serialize a JSON object to a string.

    $.ajax(

    {

        type: 'PUT',

        url: dataServiceUri + "/Travels(PartitionKey='" + this.PartitionKey + "',RowKey=guid'" + this.RowKey + "')",

        contentType: 'application/json; charset=utf-8',

        data: JSON.stringify(item.Value),

        datatype: 'json',

        error: function () {

            $('#ErrorInforamtion').text('Error retrieving data. Please try again later.');

        }

    });

Insert

To insert a new entity, you issue a POST request. The request body is identical to PUT. So the only differences are the HTTP verb and the URI. You don't need to specify the keys in URI, since the entity doesn't exist yet.

    $.ajax(

    {

        type: 'POST',

        url: dataServiceUri + '/Travels',

        contentType: 'application/json; charset=utf-8',

        data: JSON.stringify(item.Value),

        datatype: 'json',

        error: function () {

            $('#ErrorInforamtion').text('Error retrieving data. Please try again later.');

        }

    });

Delete

Finally to delete an entity, of course issue a DELETE request. You also need to specify the keys in the URI. DELETE requests do not contain request bodies.

    $.ajax(

    {

        type: 'DELETE',

        url: dataServiceUri + "/Travels(PartitionKey='" + this.PartitionKey + "',RowKey=guid'" + this.RowKey + "')",

        contentType: 'application/json; charset=utf-8',

        datatype: 'json',

        error: function () {

            $('#ErrorInforamtion').text('Error retrieving data. Please try again later.');

        }

    });

Track entity states

When working with .NET/Silverlight clients, the OData client libraries take care of entity state tracking for you. This means you can insert/update/delete an entity to a list in the client's memory. Then you can either discard the modification, or invoke the SaveChanges method to make all requests to the server. This is a useful feature. Unfortunately it is not supported out of box by AJAX clients. So you have to manually track the entity states. You can do so by adding modified items to arrays. We won't go into the details in this post. But we have a sample implementation in our source code, which will be released later.

Conclusion

This post described how to access OData Services from AJAX clients. At this point, our HTML client is almost feature complete, and we're able to connect it to our cloud service. The next post will describe how to integrate federated authentication to your applications.