Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Hosted-services / Azure

Generic wrapper for the Azure TableService

4.80/5 (2 votes)
31 Jul 2013CPOL1 min read 18.9K  
This is a generic wrapper for CRUD functions to Azure TableStorage

Introduction

This is the first article I have written in a very long time. I wanted to have a generic API to Azure Table Store. Since I could not find one here is my first attempt at writing one based on the sample code from http://www.windowsazure.com/en-us/develop/net/how-to-guides/table-services/.

Using the code

I wanted to simplify my code such that details of where the table store is and the connections details are hidden. I also wanted a simply generic set of methods that accept classes inheriting from TableEntity to do simple CRUD operations. Below is how such a DTO object looks like

C#
public class SomeDataItem : TableEntity
{
    public string String1 { get; set; }

    public string String2 { get; set; }

    public SomeDataItem()
    {
    }

    public SomeDataItem(string PartitionKey, string RowKey)
    {
        this.PartitionKey = PartitionKey;
        this.RowKey = RowKey;
    }
}

The best way to model how an API should look is from a unit test. The code below is more of an integration test but shows how I want the TableStorage class to be used. It starts by inserting an object into the table store and then carries out CRUD operations. Notice that the only thing the code passes into the class is the TableName, all the rest is handled in the App.config.

C#
[TestMethod]
public void TableStorageTest()
{
    // Definition of where our tablestore is
    string TableName = "MyTable";

    // Instanciate or TableStorage class
    TableStorage t = new TableStorage(TableName);

    // Create an object that inherits from TableEntity to be inserted into our table
    string partitionKey = "partitionkey1";
    string rowKey = "rowkey1";

    SomeDataItem data = new SomeDataItem(partitionKey ,rowKey);
    data.String1 = "test.txt";
    data.String2 = "contents";

    t.Insert<SomeDataItem>(data);

    // Check is we can read this data back
    SomeDataItem data2 = t.GetSingle<SomeDataItem>(partitionKey, rowKey);

    Assert.IsTrue(data.String2 == data2.String2);
    Assert.IsTrue(data.String1 == data2.String1);

    List<SomeDataItem> listTableFileItem = t.GetAll<SomeDataItem>(partitionKey);
    Assert.IsTrue(listTableFileItem.Count == 1);

    // Check if we can modify this data

    data2.String2 = "NewContents";
    t.Replace<SomeDataItem>(data2.PartitionKey, data2.RowKey, data2, true);
    data = t.GetSingle<SomeDataItem>(partitionKey, rowKey);

    Assert.IsTrue(data.String2 == data2.String2);
    Assert.IsTrue(data.String1 == data2.String1);

    // Remove the data
    t.DeleteEntry<SomeDataItem>(partitionKey, rowKey, data2);

    // Remove the table
    t.DeleteTable();

}

Below is how the TableStorage class looks. As can be seen I have simply wrapped the sample code in some generic classes.

XML
using System;
using System.Collections.Generic;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
namespace DataStore
{
    /// <summary>
    /// Simple Table storage generic helper class
    /// </summary>
    public class TableStorage
    {
        /// <summary>
        /// Handle to the Azure table.
        /// </summary>
        /// <value>
        /// The table.
        /// </value>
        private CloudTable table { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="TableStorage"/> class.
        /// </summary>
        /// <param name="TableName">Name of the table.</param>
        public TableStorage(string TableName)
        {
            // Retrieve the storage account from the connection string.
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
                CloudConfigurationManager.GetSetting("StorageConnectionString"));

            // Create the table client.
            CloudTableClient tableClient = storageAccount.CreateCloudTableClient();

            // Create the table if it doesn't exist.
            this.table = tableClient.GetTableReference(TableName);
            table.CreateIfNotExists();
        }

        /// <summary>
        /// Inserts the specified data.
        /// </summary>
        /// <typeparam name="T">DTO that inherits from TableEntity</typeparam>
        /// <param name="data">The data.</param>
        public void Insert<T>(T data) where T : TableEntity
        {
            // Create the TableOperation that inserts the customer entity.
            TableOperation insertOperation = TableOperation.Insert(data);

            // Execute the insert operation.
            this.table.Execute(insertOperation);
        }

        /// <summary>
        /// Inserts a list of table entries as a batch.
        /// </summary>
        /// <typeparam name="T">DTO that inherits from TableEntity</typeparam>
        /// <param name="data">The data.</param>
        public void InsertBatch<T>(List<T> data) where T : TableEntity
        {
            // Create the batch operation.
            TableBatchOperation batchOperation = new TableBatchOperation();

            // Add both customer entities to the batch insert operation.
            foreach (TableEntity d in data)
            {
                batchOperation.Insert(d);
            }

            // Execute the batch operation.
            table.ExecuteBatch(batchOperation);
        }

        /// <summary>
        /// Gets all data corresponding to a partition key.
        /// </summary>
        /// <typeparam name="T">DTO that inherits from TableEntity</typeparam>
        /// <param name="PartitionKey">The partition key.</param>
        /// <returns>A list of T that has the corresponding partion key</returns>
        public List<T>GetAll<T>(string PartitionKey) where T : TableEntity
        {
            // Construct the query operation for all customer entities where PartitionKey="Smith".
            TableQuery<SomeDataItem> query = new TableQuery<SomeDataItem>().Where(
              TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, PartitionKey));

            List<T> Results = new List<T>();
            // Print the fields for each customer.
            foreach(SomeDataItem entity in table.ExecuteQuery(query))
            {
                Results.Add(entity as T);
                //Console.WriteLine("{0}, {1}\t{2}\t{3}", entity.PartitionKey, entity.RowKey,
                //    entity.Email, entity.PhoneNumber);
            }
            return Results;
        }

        /// <summary>
        /// Gets the single.
        /// </summary>
        /// <typeparam name="T">DTO that inherits from TableEntity</typeparam>
        /// <param name="PartitionKey">The partition key.</param>
        /// <param name="RowKey">The row key.</param>
        /// <returns></returns>
        public T GetSingle<T>(string PartitionKey, string RowKey ) where T : TableEntity
        {

            // Create a retrieve operation that takes a customer entity.
            TableOperation retrieveOperation = TableOperation.Retrieve<T>(PartitionKey, RowKey);

            // Execute the retrieve operation.
            TableResult retrievedResult = table.Execute(retrieveOperation);

            T result = null;
            // Print the phone number of the result.
            if (retrievedResult.Result != null)
            {
                result = retrievedResult.Result as T;
            }
            return result;
        }

        /// <summary>
        /// Replaces the specified partition key.
        /// </summary>
        /// <typeparam name="T">DTO that inherits from TableEntity</typeparam>
        /// <param name="PartitionKey">The partition key.</param>
        /// <param name="RowKey">The row key.</param>
        /// <param name="ReplacementData">The replacement data.</param>
        /// <param name="InsertOrReplace">The insert O replace.</param>
        public void Replace<T>(string PartitionKey, string RowKey, 
               T ReplacementData, Boolean InsertOrReplace) where T : TableEntity
        {
            // Create a retrieve operation that takes a customer entity.
            TableOperation retrieveOperation = TableOperation.Retrieve<T>(PartitionKey, RowKey);

            // Execute the operation.
            TableResult retrievedResult = table.Execute(retrieveOperation);

            // Assign the result to a CustomerEntity object.
            T updateEntity = retrievedResult.Result as T;

            if (updateEntity != null)
            {
                ReplacementData.PartitionKey = updateEntity.PartitionKey;
                ReplacementData.RowKey = updateEntity.RowKey;

                // Create the InsertOrReplace TableOperation
                TableOperation updateOperation;
                if (InsertOrReplace)
                {
                    updateOperation = TableOperation.InsertOrReplace(ReplacementData);
                }
                else
                {
                    updateOperation = TableOperation.Replace(ReplacementData);
                }

                // Execute the operation.
                table.Execute(updateOperation);

                Console.WriteLine("Entity updated.");
            }

            else
                Console.WriteLine("Entity could not be retrieved.");
        }

        /// <summary>
        /// Deletes the entry.
        /// </summary>
        /// <typeparam name="T">DTO that inherits from TableEntity</typeparam>
        /// <param name="PartitionKey">The partition key.</param>
        /// <param name="RowKey">The row key.</param>
        /// <param name="ReplacementData">The replacement data.</param>
        public void DeleteEntry<T>(string PartitionKey, string RowKey, T ReplacementData) where T : TableEntity
        {

            // Create a retrieve operation that expects a customer entity.
            TableOperation retrieveOperation = TableOperation.Retrieve<T>(PartitionKey, RowKey);

            // Execute the operation.
            TableResult retrievedResult = table.Execute(retrieveOperation);

            // Assign the result to a CustomerEntity.
            T deleteEntity = retrievedResult.Result as T;

            // Create the Delete TableOperation.
            if (deleteEntity != null)
            {
                TableOperation deleteOperation = TableOperation.Delete(deleteEntity);

                // Execute the operation.
                table.Execute(deleteOperation);

                Console.WriteLine("Entity deleted.");
            }

            else
                Console.WriteLine("Could not retrieve the entity.");

        }

        /// <summary>
        /// Deletes the table.
        /// </summary>
        public void DeleteTable()
        {
            // Delete the table it if exists.
            table.DeleteIfExists();
        }
    }
}

Conclusion

I am sure there are many ways to improve this. For example I could make the class Asynchronous, I could handle the transfer of large data with some progress information etc. What I wanted to demonstrate is how to use generic functions to simplify CRUD Table operations. I hope this is useful.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)