Saturday, November 22, 2008

Azure Storage Services Test Harness: Table Services 6 – Paging LINQ to REST Query Result Sets

Update 1/31/2009: Code is now available for download.

This is the sixth of a series of articles about a ASP.NET 3.5 C# test harness for Azure Storage Services that is will be available for download from my “Retire Your Data Center” cover story for Visual Studio Magazine’s February 2009 issue (click the Get Code Download link) in the near future.

Azure Table Services HTTP Requests include a $top=n system query option that accepts NextPartitionKey and NewRowKey parameters to position the starting point of successive GET queries to the appropriate entity. HTTP Responses return the parameter values for the next retrieval. The feature implements a basic paging capability, where n is the number of entities returned. This article shows the C# 3.0 source code to implement paging of an ASP.NET GridView control’s contents.

The article also shows sample code for performing iterative CRUD operations on the CustomerTable’s entities.

Preceding episodes of this Azure Storage Services Test Harness series are:

Request and Response Headers with Starting Position

Following is a typical HTTP Request header that uses the table primary key value as the RowKey:

GET /CustomerTable()?$top=7&NextPartitionKey=Customers&NextRowKey=BOLID HTTP/1.1
User-Agent: Microsoft ADO.NET Data Services
x-ms-date: Sat, 22 Nov 2008 18:29:56 GMT
Authorization: SharedKeyLite oakleaf:t8J4j1oPPYuaPLzyjnFhJD0GZUATFhhwiTqdygkNysE=
Accept: application/atom+xml,application/xml
Accept-Charset: UTF-8
DataServiceVersion: 1.0;NetFx
MaxDataServiceVersion: 1.0;NetFx
Host: oakleaf.table.core.windows.net

Here’s the response header with the first and last CustomerTable entity of the page:

HTTP/1.1 200 OK
Cache-Control: no-cache
Transfer-Encoding: chunked
Content-Type: application/atom+xml;charset=utf-8
Server: Table Service Version 1.0 Microsoft-HTTPAPI/2.0
x-ms-request-id: 57cbcfb9-5e2f-407e-846d-69c06b7f12bc
x-ms-continuation-NextPartitionKey: Customers
x-ms-continuation-NextRowKey: COMMI
Date: Sat, 22 Nov 2008 18:29:55 GMT

24E1
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="http://oakleaf.table.core.windows.net/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
  <title type="text">CustomerTable</title>
  <id>http://oakleaf.table.core.windows.net/CustomerTable</id>
  <updated>2008-11-22T18:29:55Z</updated>
  <link rel="self" title="CustomerTable" href="CustomerTable" />
  <entry m:etag="W/&quot;datetime'2008-11-12T23%3A08%3A46.0506837Z'&quot;">
    <id>http://oakleaf.table.core.windows.net/CustomerTable(PartitionKey='Customers',RowKey='BOLID')</id>
    <title type="text"></title>
    <updated>2008-11-22T18:29:55Z</updated>
    <author>
      <name />
    </author>
    <link rel="edit" title="CustomerTable" href="CustomerTable(PartitionKey='Customers',RowKey='BOLID')" />
    <category term="oakleaf.CustomerTable" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <content type="application/xml">
      <m:properties>
        <d:PartitionKey>Customers</d:PartitionKey>
        <d:RowKey>BOLID</d:RowKey>
        <d:Timestamp m:type="Edm.DateTime">2008-11-12T23:08:46.0506837Z</d:Timestamp>
        <d:Address>C/ Araquil, 67</d:Address>
        <d:City>Madrid</d:City>
        <d:CompanyName>Bólido Comidas preparadas</d:CompanyName>
        <d:ContactName>Martín Sommer</d:ContactName>
        <d:ContactTitle>Owner</d:ContactTitle>
        <d:Country>Spain</d:Country>
        <d:CustomerID>BOLID</d:CustomerID>
        <d:Fax>(91) 555 91 99</d:Fax>
        <d:Phone>(91) 555 22 82</d:Phone>
        <d:PostalCode>28023</d:PostalCode>
      </m:properties>
    </content>
  </entry>
...
  <entry m:etag="W/&quot;datetime'2008-11-12T23%3A08%3A46.8546837Z'&quot;">
    <id>http://oakleaf.table.core.windows.net/CustomerTable(PartitionKey='Customers',RowKey='CHOPS')</id>
    <title type="text"></title>
    <updated>2008-11-22T18:29:55Z</updated>
    <author>
      <name />
    </author>
    <link rel="edit" title="CustomerTable" href="CustomerTable(PartitionKey='Customers',RowKey='CHOPS')" />
    <category term="oakleaf.CustomerTable" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <content type="application/xml">
      <m:properties>
        <d:PartitionKey>Customers</d:PartitionKey>
        <d:RowKey>CHOPS</d:RowKey>
        <d:Timestamp m:type="Edm.DateTime">2008-11-12T23:08:46.8546837Z</d:Timestamp>
        <d:Address>Hauptstr. 29</d:Address>
        <d:City>Bern</d:City>
        <d:CompanyName>Chop-suey Chinese</d:CompanyName>
        <d:ContactName>Yang Wang</d:ContactName>
        <d:ContactTitle>Owner</d:ContactTitle>
        <d:Country>Switzerland</d:Country>
        <d:CustomerID>CHOPS</d:CustomerID>
        <d:Phone>0452-076545</d:Phone>
        <d:PostalCode>3012</d:PostalCode>
      </m:properties>
    </content>
  </entry>
</feed>
0

The next CustomerTable entity in sequence is COMMI (Comércio Mineiro).

Implementing Paging in Default.aspx.cs

The following code in the Default.aspx.cs class file’s Page_Prerender event handler adds the starting NextPartitionKey and NextRowKey values to the query string parameters by invoking the AddQueryOption() method, executes the query. The code is based on Steve Marx’s Paging Over Data in Windows Azure Tables post of 11/12/2008.

The code then extracts the successive NextPartitionKey and NextRowKey values for use in the query for the next page, as shown here:

Here’s the page generated by the Development Fabric and populated from Cloud Storage (http:oakleaf.table.core.windows.net)  by the preceding code (click the image to display a full-size capture):

Counting, Deleting, Creating, and Updating Table Data

The Default.aspx.cs class file also contains event handlers for the Count Customers, Delete All Customers, Create Customers and Update Customers buttons, to perform iterative CRUD operations on the CustomerTable EntitySet. Creating Customer entities takes advantage of the classes and collection initializers generated by the LINQ In-Memory Object Generator (LIMOG) v2, which the Table Services 5 – Generating Classes/Collection Initializers with LIMOG v2 article describes.

Here’s the code for the four event handlers:

2 comments:

Anonymous said...

Here, why are you not using the DataSource class you created. All the examples are without using that class.

Roger Jennings (--rj) said...

@Anon,

The page's ObjectDataSource invokes the CustomerDataSource class's Select(), Delete(), and Insert() methods. I haven't provided Insert() or Delete() buttons for the public demo.