Gaurav Mantri's Personal Blog.

Storage Client Library 2.0 – Migrating Table Storage Code

Recently with the release of SDK 1.8 for Windows Azure, the Windows Azure Storage Team announced the availability of the next version of storage client library (version 2.0). The new library is significantly different than the previous version and from what I know, it has been written from ground up to achieve better performance and is more user friendly. Because it is significantly different, upgrading from previous version (1.7) to this version is not trivial.

In this blog post, I will attempt to provide some code sample through which I will try and demonstrate how you can do some common tasks when working with Azure Table Storage. What I did is wrote two simple console applications: one which uses storage client library version 1.7 and the other which uses version 2.0 and in those two applications I demonstrated some simple functionality.

Read These First

Since version 2.0 library is significantly different than the previous ones, before you decide to upgrade your code to make use of this version I strongly urge you to read up the following blog posts by the storage team as there’re many breaking changes.

Introducing Windows Azure Storage Client Library 2.0 for .NET and Windows Runtime

http://blogs.msdn.com/b/windowsazurestorage/archive/2012/10/29/introducing-windows-azure-storage-client-library-2-0-for-net-and-windows-runtime.aspx

Windows Azure Storage Client Library 2.0 Breaking Changes & Migration Guide

http://blogs.msdn.com/b/windowsazurestorage/archive/2012/10/29/windows-azure-storage-client-library-2-0-breaking-changes-amp-migration-guide.aspx

Windows Azure Storage Client Library 2.0 Tables Deep Dive

http://blogs.msdn.com/b/windowsazurestorage/archive/2012/11/06/windows-azure-storage-client-library-2-0-tables-deep-dive.aspx

Getting Started

Before jumping into the code, there’re a few things I would like to mention:

Storage Client Libraries

To get the reference for storage client library 1.7, you can browse your local computer and navigate to the Azure SDK installation directory (C:\Program Files\Microsoft SDKs\Windows Azure\.NET SDK\2012-10\ref – assuming you have SDK 1.8 installed) and select Microsoft.WindowsAzure.StorageClient.dll from there. For SDK 1.7 project, you would also need to add reference to System.Data.Services.Client.dll as well.

To get the reference for storage client library 2.0 (or the latest version for that matter), I would actually recommend getting this using Nuget. That way you’ll always get the latest version. You can simply get it by executing the following command in Nuget Package Manager console: Install-Package WindowsAzure.Storage. While it’s an easy way to get the latest version upgrades, one must not upgrade it before ensuring the new version won’t break anything in the existing code.

Sample Entity

For the purpose of demonstration, I created a simple entity called “CustomerEntity”. With version 1.7, it derives from TableServiceEntity class and with version 2.0, it derives from TableEntity class. Please note that with version 2.0, storage team has also made available a DynamicTableEntity class which comes in handy to fully exploit the No-SQL schema-less support in Azure Table Storage. While TableEntity and other mechanisms also fully exploits the N0-SQL schema-less support in Azure Table Storage, DynamicTableEntity comes in real handy when you don’t know about the object model before hand. In this blog post, we will focus on TableEntity though. Also for the sake of simplicity, I have kept the PartitionKey as “Customer” and assigned a GUID for RowKey.

For version 1.7, the customer entity looks something like this:

 
    public class CustomerEntity : TableServiceEntity
    {
        public CustomerEntity()
        {
            PartitionKey = "Customer";
            RowKey = Guid.NewGuid().ToString();
        }

        public string FirstName
        {
            get;
            set;
        }

        public string LastName
        {
            get;
            set;
        }

        public DateTime? LastOrderDate
        {
            get;
            set;
        }
    }

For version 2.0, the customer entity looks something like this:

 

    public class CustomerEntity : TableEntity
    {
        public CustomerEntity()
        {
            PartitionKey = "Customer";
            RowKey = Guid.NewGuid().ToString();
        }

        public string FirstName
        {
            get;
            set;
        }

        public string LastName
        {
            get;
            set;
        }

        public DateTime? LastOrderDate
        {
            get;
            set;
        }
    }

Operations

Now let’s see how you can perform some operations. What I’ve done is first showed how you did an operation with version 1.7 and then how would you do the same operation with version 2.0.

Create Table

If you’re using the following code with version 1.7 to create a table:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            cloudTableClient.CreateTableIfNotExist(tableName);

You would use something like this with version 2.0 to achieve the same:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            CloudTable table = cloudTableClient.GetTableReference(tableName);
            table.CreateIfNotExists();

Delete Table

If you’re using the following code with version 1.7 to delete a table:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            cloudTableClient.DeleteTableIfExist(tableName);

You would use something like this with version 2.0 to achieve the same:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            CloudTable table = cloudTableClient.GetTableReference(tableName);
            table.DeleteIfExists();

Insert Entity

If you’re using the following code with version 1.7 to insert an entity:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            var customer = new CustomerEntity()
            {
                FirstName = "John",
                LastName = "Smith",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-10)
            };

            var serviceContext = cloudTableClient.GetDataServiceContext();
            serviceContext.AddObject(tableName, customer);
            serviceContext.SaveChangesWithRetries();

You would use something like this with version 2.0 to achieve the same:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            var customer = new CustomerEntity()
            {
                FirstName = "John",
                LastName = "Smith",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-10)
            };

            CloudTable table = cloudTableClient.GetTableReference(tableName);
            TableOperation insertOperation = TableOperation.Insert(customer);
            table.Execute(insertOperation);

Delete Entity

If you’re using the following code with version 1.7 to delete an entity:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            CustomerEntity customer = GetCustomerEntityFromSomePlace();

            var serviceContext = cloudTableClient.GetDataServiceContext();
            serviceContext.AttachTo(tableName, customer, "*");
            serviceContext.DeleteObject(customer);
            serviceContext.SaveChangesWithRetries();

You would use something like this with version 2.0 to achieve the same:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            CustomerEntity customer = GetCustomerEntityFromSomePlace();

            CloudTable table = cloudTableClient.GetTableReference(tableName);
            TableOperation deleteOperation = TableOperation.Delete(customer);
            table.Execute(deleteOperation);

Replace Entity

If you’re using the following code with version 1.7 to replace an entity:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            CustomerEntity customer = GetCustomerEntityFromSomePlace();
            customer.LastOrderDate = null;
            var serviceContext = cloudTableClient.GetDataServiceContext();
            serviceContext.AttachTo(tableName, customer, "*");
            serviceContext.UpdateObject(customer);
            serviceContext.SaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate);

You would use something like this with version 2.0 to achieve the same:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            CustomerEntity customer = GetCustomerEntityFromSomePlace();
            customer.LastOrderDate = null;
            CloudTable table = cloudTableClient.GetTableReference(tableName);
            TableOperation replaceOperation = TableOperation.Replace(customer);
            table.Execute(replaceOperation);

Merge Entity

If you’re using the following code with version 1.7 to merge an entity:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            CustomerEntity customer = GetCustomerEntityFromSomePlace();
            customer.LastOrderDate = DateTime.UtcNow;
            var serviceContext = cloudTableClient.GetDataServiceContext();
            serviceContext.AttachTo(tableName, customer, "*");
            serviceContext.UpdateObject(customer);
            serviceContext.SaveChangesWithRetries();

You would use something like this with version 2.0 to achieve the same:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            CustomerEntity customer = GetCustomerEntityFromSomePlace();
            customer.LastOrderDate = DateTime.UtcNow;
            CloudTable table = cloudTableClient.GetTableReference(tableName);
            TableOperation mergeOperation = TableOperation.Merge(customer);
            table.Execute(mergeOperation);

Insert or Replace Entity

If you’re using the following code with version 1.7 to insert or replace an entity:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            var customer = new CustomerEntity()
            {
                FirstName = "John",
                LastName = "Smith",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-10)
            };
            var serviceContext = cloudTableClient.GetDataServiceContext();
            serviceContext.AttachTo(tableName, customer, null);
            serviceContext.UpdateObject(customer);
            serviceContext.SaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate);

You would use something like this with version 2.0 to achieve the same:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            var customer = new CustomerEntity()
            {
                FirstName = "John",
                LastName = "Smith",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-10)
            };
            CloudTable table = cloudTableClient.GetTableReference(tableName);
            TableOperation insertOrReplaceOperation = TableOperation.InsertOrReplace(customer);
            table.Execute(insertOrReplaceOperation);

Insert or Merge Entity

If you’re using the following code with version 1.7 to insert or merge an entity:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            var customer = new CustomerEntity()
            {
                FirstName = "John",
                LastName = "Smith",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-10)
            };
            var serviceContext = cloudTableClient.GetDataServiceContext();
            serviceContext.AttachTo(tableName, customer, null);
            serviceContext.UpdateObject(customer);
            serviceContext.SaveChangesWithRetries();

You would use something like this with version 2.0 to achieve the same:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            var customer = new CustomerEntity()
            {
                FirstName = "John",
                LastName = "Smith",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-10)
            };
            CloudTable table = cloudTableClient.GetTableReference(tableName);
            TableOperation insertOrMergeOperation = TableOperation.InsertOrMerge(customer);
            table.Execute(insertOrMergeOperation);

Entity Batch Operation

As you know, Azure Table Storage supports entity batch transactions to manage multiple entities in a single transaction. Assuming you’re trying to insert some entities in an Azure table using entity batch transaction. If you’re using the following code to perform bulk insert with version 1.7:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            var customer = new CustomerEntity()
            {
                FirstName = "John",
                LastName = "Smith",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-10)
            };
            var serviceContext = cloudTableClient.GetDataServiceContext();
            serviceContext.AddObject(tableName, customer);
            customer = new CustomerEntity()
            {
                FirstName = "Jane",
                LastName = "Smith",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-5)
            };
            serviceContext.AddObject(tableName, customer);
            customer = new CustomerEntity()
            {
                FirstName = "John",
                LastName = "Doe",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-7)
            };
            serviceContext.AddObject(tableName, customer);
            customer = new CustomerEntity()
            {
                FirstName = "Jane",
                LastName = "Doe",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-3)
            };
            serviceContext.AddObject(tableName, customer);
            serviceContext.SaveChangesWithRetries(SaveChangesOptions.Batch);

You would use something like this with version 2.0 to achieve the same:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            var customer = new CustomerEntity()
            {
                FirstName = "John",
                LastName = "Smith",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-10)
            };
            CloudTable table = cloudTableClient.GetTableReference(tableName);
            TableBatchOperation batchOperation = new TableBatchOperation();
            batchOperation.Insert(customer);
            customer = new CustomerEntity()
            {
                FirstName = "Jane",
                LastName = "Smith",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-5)
            };
            batchOperation.Insert(customer);
            customer = new CustomerEntity()
            {
                FirstName = "John",
                LastName = "Doe",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-7)
            };
            batchOperation.Insert(customer);
            customer = new CustomerEntity()
            {
                FirstName = "Jane",
                LastName = "Doe",
                LastOrderDate = DateTime.UtcNow.Date.AddDays(-3)
            };
            batchOperation.Insert(customer);
            table.ExecuteBatch(batchOperation);

Fetching Single Entity

If you’re using the following code with version 1.7 to fetch a single entity by PartitionKey and RowKey:

 
            string partitionKey = “<some PartitionKey value>”;
            string rowKey = “<some RowKey value>”;
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            var serviceContext = cloudTableClient.GetDataServiceContext();
            IQueryable<CustomerEntity> customerQuery = (from entity in serviceContext.CreateQuery<CustomerEntity>(tableName) where entity.PartitionKey == partitionKey && entity.RowKey == rowKey select entity);
            var returnedCustomer = customerQuery.FirstOrDefault();

You would use something like this with version 2.0 to achieve the same:

 
            string partitionKey = “<some PartitionKey value>”;
            string rowKey = “<some RowKey value>”;
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            CloudTable table = cloudTableClient.GetTableReference(tableName);
            TableOperation retrieveOperation = TableOperation.Retrieve<CustomerEntity>(partitionKey, rowKey);
            TableResult retrievedResult = table.Execute(retrieveOperation);
            CustomerEntity fetchedCustomer = retrievedResult.Result as CustomerEntity;

Querying Entities

If you’re using the following code with version 1.7 to fetch entities by say filtering the records on last name attribute of our Customer entity:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            var serviceContext = cloudTableClient.GetDataServiceContext();
            //Specify the filter condition on LastName == 'Smith'
            IQueryable<CustomerEntity> customerQuery = (from entity in serviceContext.CreateQuery<CustomerEntity>(tableName) where entity.LastName == "Smith" select entity);
            var result = customerQuery.ToList();

You would use something like this with version 2.0 to achieve the same:

 
            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudTableClient cloudTableClient = storageAccount.CreateCloudTableClient();
            CloudTable table = cloudTableClient.GetTableReference(tableName);
            //Specify the filter condition on LastName == 'Smith'
            TableQuery<CustomerEntity> query = (new TableQuery<CustomerEntity>()).Where(TableQuery.GenerateFilterCondition("LastName", QueryComparisons.Equal, "Smith"));
            var result = table.ExecuteQuery<CustomerEntity>(query);

Closing Thoughts

As you saw, there’re significant differences between version 1.7 and 2.0 and one need to put some extra thoughts before undertaking the migration however in my opinion the version 2.0 library is much more straight forward to understand and is more intuitive. For example, to create an entity we would use TableOperation.Insert(entity).

Though I did not cover it in this blog post, but you can still use the legacy DataServiceClient approach with version 2.0. Do note that DataServiceClient approach still makes use of WCF services approach. Using TableEntity approach offers significant performance and latency improvements over the WCF Data Services. One thing with DataServiceClient approach is that you’re only allowed to perform single operation per data context. Furthermore the DataServiceClient approach is not available in library for Windows 8. I haven’t tried it myself but it seems we just have to change namespaces under the using section in our source files and everything else should work as is. That would certainly ease the pain in the short run however my personal recommendation is to “take the pill” 🙂 and make use of TableEntity if you can afford it.

Finally, don’t give up on Storage Client Library 1.7 just yet. There’re still some components which depend on 1.7 version. Good example is Windows Azure Diagnostics which still depends on the older version at the time of writing this blog. Good thing is that both version 1.7 and 2.0 can co-exist in a project.

Source Code

You can download the source code for this project from here: Sample Project Source Code

Acknowledgement

I’m thankful to Aymeric Lagier, Richard Conway, and Jai Haridas & Joe Giardino from Windows Azure Storage Team to take time out and reviewing my post.

Summary

The examples I presented in this post are quite basic but hopefully they should give you an idea about how to use the latest version of storage client library. In general, I am quite pleased with the changes the team has done though I wish there was an easier migration path. Please feel free to share your experience with migration exercise by providing comments. This will help me and the readers of this blog immensely. Finally, if you find any issues with this post please let me know and I will try and fix them ASAP.

Happy Coding!


[This is the latest product I'm working on]