Building Windows Azure Service Part3: Table Storage

This post shows how to create a project that contains the classes to store guest entries in Windows Azure Table Storage.

The Table Storage service offers semi-structured storage in the form of tables that contain collections of entities. Entities have a primary key and a set of properties, where a property is a (name, typed-value) pair. The Table Storage service primary key has the following two properties:

  • PartitionKey and RowKey keys that uniquely identify each entity in the table.
  • Every entity in the Table Storage service also has a Timestamp system property, which allows the service to keep track of when an entity was last modified. This Timestamp field is intended for system use and should not be accessed by the application.

The Table Storage API provides a TableServiceEntity class that defines the necessary properties, which you can use as the base class for your entities. This API is compliant with the REST API provided by Data Services and allows you to use the.NET Client Library for Data Services to work with data in the Table service using .NET objects.

Although the Table Storage does not enforce any schema for tables, which makes it possible for two entities in the same table to have different sets of properties, the GuestBook application uses a fixed schema to store its data.

In order to use the.NET Client Library for Data Services to access the data in Table Storage, you need to create a context class that derives from TableServiceContext, which itself derives from DataServiceContext in .NET Client Library for Data Services .

The Table Storage API allows applications to create the tables from the context class. For this to happen, the context class must expose each required table as a property of type IQueryable<SchemaClass> , where SchemaClass is the class that models the entities stored in the table.

In this post, you will perform the following tasks:

To create the data model project
  1. In Solution Explorer dialog right-click the GuestBook solution and select New Project.

  2. In the Add New Project dialog window, choose the Windows category.

  3. Select Class Library in the templates list.

  4. In the Name box enter the GuestBook_Data.

  5. Click OK.

    image

    Figure 5 Creating the Data Model Project

  6. In Solution Explorer right-click the GuestBook_Data project.

  7. Select Add Reference.

  8. In the Add Reference window dialog select .NET tab.

  9. Add reference to System.Data.Services.Client.

  10. Repeat the previous steps to add a reference to Microsoft.WindowsAzure.StorageClient.

  11. Delete the default Class1.cs generated by the template.

To create the schema class

This section shows how to create the class that defines the schema for the GuestBook table entries. Its parent class Microsoft.WindowsAzure.StorageClient.TableServiceEntity defines the properties required by every entity stored in a Windows Azure table.

  1. Right-click the GuestBookData project, click Add New item.
  2. In the Add New Item dialog window, select the Code category.
  3. In the templates list select Class.
  4. In the Name box enter GuestBookEntry.cs.
  5. Click Add.
  6. Open the GuestBookEntry.cs file and replace its content with the following code.
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure.StorageClient;

namespace GuestBook_Data
{
    /// <summary>
    /// The GuestBookEntry class defines the schema for each entry (row) 
    /// of the guest book table that stores guest information.
    /// <remarks>
    /// The parent class TableServiceEntity defines the properties required 
    /// by every entity that uses the Windows Azure Table Services. These      
    /// properties include PartitionKey and RowKey.
    /// </remarks>
    /// </summary>
    public class GuestBookEntry :
        Microsoft.WindowsAzure.StorageClient.TableServiceEntity
    {
        /// <summary>
        /// Create a new instance of the GuestBookEntry class and 
        /// initialize the requires keys.
        /// </summary>
        public GuestBookEntry()
        {
            // The partition key allows partitioning the data so that
            // there is a separate partition for each day of guest 
            // book entries. The partition key is used to assure 
            // load balancing for data access across fabric nodes (servers).
            PartitionKey = DateTime.UtcNow.ToString("MMddyyyy");

            // Row key allows sorting, this assures the rows are 
            // returned in time order.
            RowKey = string.Format("{0:10}_{1}",
                DateTime.MaxValue.Ticks - DateTime.Now.Ticks, Guid.NewGuid());
        }

        // Define the properties that contain guest information.
        public string Message { get; set; }
        public string GuestName { get; set; }
        public string PhotoUrl { get; set; }
        public string ThumbnailUrl { get; set; }
    }
}

To create the context class

This section shows how to create the context class that enables Windows Azure Table Storage services. Its parent class Microsoft.WindowsAzure.StorageClient.TableserviceContext manages the credentials required to access your account and provides support for a retry policy for update operations.

  1. Right-click the GuestBookData project, click Add New item.
  2. In the Add New Item dialog window, select the Code category.
  3. In the templates list select Class.
  4. In the Name box enter GuestBookDataContext.cs.
  5. Click Add.
  6. Open the GuestBookDataContext.cs file and replace its content with the following code.
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace GuestBook_Data
{
    /// <summary>
    /// The GuestBookDataContext class enables Windows 
    /// Azure Table Services.
    /// <remarks>
    /// The parent class TableserviceContext manages the credentials 
    /// required to access account information and provides support for a 
    /// retry policy for update operations.
    /// </remarks> 
    /// </summary>
    public class GuestBookDataContext : TableServiceContext
    {
        /// <summary>
        /// Create an instance of the GuestBookDataContext class 
        /// and initialize the base
        /// class with storage access information.
        /// </summary>
        /// <param name="baseAddress"></param>
        /// <param name="credentials"></param>
        public GuestBookDataContext(string baseAddress,
            StorageCredentials credentials)
            : base(baseAddress, credentials)
        { }

        /// <summary>
        /// Define the property that returns the GuestBookEntry table.
        /// </summary>
        public IQueryable<GuestBookEntry> GuestBookEntry
        {
            get
            {
                return this.CreateQuery<GuestBookEntry>("GuestBookEntry");
            }
        }
    }
}

To create the data source class

This section shows how to create the data source class that allows for the creation of data source objects that can be bound to ASP.NET data controls and allow for the user to interact with the Table Storage.

  1. Right-click the GuestBookData project, click Add New item.
  2. In the Add New Item dialog window, select the Code category.
  3. In the templates list select Class.
  4. In the Name box enter GuestBookDataSource.cs.
  5. Click Add.
  6. Open the GuestBookDataSource.cs file and replace its content with the following code.

 

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace GuestBook_Data
{
    /// <summary>
    /// The GuestBookEntryDataSource class allows for the creation of data 
    /// source objects that can be bound to ASP.NET data controls. These 
    /// controls enable the user to perform Create, Read, Update and Delete 
    /// (CRUD) operations. 
    /// </summary>
    public class GuestBookEntryDataSource
    {
        // Storage services account information.
        private static CloudStorageAccount storageAccount;

        // Context that allows the use of Windows Azure Table Services.
        private GuestBookDataContext context;

        /// <summary>
        /// Initializes storage account information and creates a table 
        /// using the defined context.
        /// <remarks>
        /// This constructor creates a table using the schema (model) 
        /// defined by the
        /// GuestBookDataContext class and the storage account information 
        /// contained in the configuration connection string settings.
        /// Declaring the constructor static assures that the initialization 
        /// tasks are performed only once.
        /// </remarks>
        /// </summary>
        static GuestBookEntryDataSource()
        {
            // Create a new instance of a CloudStorageAccount object from a specified configuration setting. 
            // This method may be called only after the SetConfigurationSettingPublisher 
            // method has been called to configure the global configuration setting publisher.
            // You can call the SetConfigurationSettingPublisher method in the OnStart method
            // of the web or worker role before calling FromConfigurationSetting.
            // If you do not do this, the system raises an exception.
            storageAccount = 
                CloudStorageAccount.FromConfigurationSetting("DataConnectionString");

            // Create table using the schema (model) defined by the
            // GuestBookDataContext class and the storage account information. 
            CloudTableClient.CreateTablesFromModel(
                typeof(GuestBookDataContext),
                storageAccount.TableEndpoint.AbsoluteUri,
                storageAccount.Credentials);
        }

        /// <summary>
        /// Initialize context used to access table storage and the retry policy.
        /// </summary>
        public GuestBookEntryDataSource()
        {
            // Initialize context using account information.
            this.context =
                new GuestBookDataContext(storageAccount.TableEndpoint.AbsoluteUri,
            storageAccount.Credentials);
            // Initialize retry update policy.
            this.context.RetryPolicy = RetryPolicies.Retry(3,
            TimeSpan.FromSeconds(1));
        }

        /// <summary>
        /// Gets the contents of the GuestBookentry table.
        /// </summary>
        /// <returns>
        /// results: the GuestBookEntry table contents.
        /// </returns>
        /// <remarks>
        /// This method retrieves today guest book entries by using the current
        /// date as the partition key. The web role uses this method to 
        /// bind the results to a data grid to display the guest book.
        /// </remarks>
        public IEnumerable<GuestBookEntry> Select()
        {
            var results = from g in this.context.GuestBookEntry
                          where g.PartitionKey ==
                DateTime.UtcNow.ToString("MMddyyyy")
                          select g;
            return results;
        }

        /// <summary>
        /// Insert new entries in the GuestBookEntry table.
        /// </summary>
        /// <param name="newItem"></param>
        public void AddGuestBookEntry(GuestBookEntry newItem)
        {

            this.context.AddObject("GuestBookEntry", newItem);
            this.context.SaveChanges();
        }

        /// <summary>
        /// Update the thumbnail URL for a table entry.
        /// </summary>
        /// <param name="partitionKey"></param>
        /// <param name="rowKey"></param>
        /// <param name="thumbUrl"></param>
        public void UpdateImageThumbnail(string partitionKey,
            string rowKey, string thumbUrl)
        {
            var results = from g in this.context.GuestBookEntry
                          where g.PartitionKey == partitionKey && g.RowKey ==
                rowKey
                          select g;

            var entry = results.FirstOrDefault<GuestBookEntry>();
            entry.ThumbnailUrl = thumbUrl;
            this.context.UpdateObject(entry);
            this.context.SaveChanges();
        }
    }
}

For related topics, see the following posts.