Looking at Azure Table Storage leaves us developers often puzzled. It’s hard to get a general idea of what it is actually for. Why do we need this NoSQL thing? Storing loads of structured data? That’s why we use SQL server, right? But it’s non-relational. Why? We’re so in love with our relational databases. But is it always necessary to store data that way?
Azure Table Storage is all about storing (possibly huge) ammounts of easy scale-able, non-relational data, blazing fast and easy queryable. Forget about relationships, performance tweaking you indices. Also forget about those DBA’s from hell
or other major constraints. Store all kinds of different entities and retrieve them where and whenever needed.
In this example we build up on the MVC 4 statistics application from the previous post on publishing your app to the cloud. We take that as a starting point. We use Azure Table Storage to present the user a list of ten former sample datasets to reuse. No rocket science. This is what it looks like:

Creating an Entity
To use Azure Table Storage you need a poco / model derived from TableServiceEntity. This represents an entity in a Windows Azure table. It adds three public properties required by Azure Table Storage.
- PartitionKey
You can see this as the category or cluster your entity belongs to. Used for load balancing. All entities with the same PartitionKey are served from the same place.
- RowKey
Provides uniqueness within a given PartitionKey. You can see it as the title or name of your entity.
- Timestamp
Read only property to make optimistic concurrency possible.
Our entity provide us with a way to store a single sample data history item to Azure Table Storage. In the constructor we hard coded our PartitionKey as SampleDataHistory and set the RowKey to a random Guid.
using System;
using Microsoft.WindowsAzure.StorageClient;
namespace StatsOnAzure.Models
{
public sealed class SampleDataHistoryItem : TableServiceEntity
{
public SampleDataHistoryItem()
{
PartitionKey = "SampleDataHistory";
RowKey = Guid.NewGuid().ToString();
}
public string SampleData { get; set; }
}
}
Setting the Connectionstring
So now we need a way to access the Azure Table Storage service. First we need to set up a connectionstring. We do this by right clicking the webrole in our Azure Service project and choose properties and the settings tab. Add and name a connectionstring and set it to UseDevelopmentStorage=true. This way we can test against our local Azure emulator. Later we set this to our actual Table Storage on Windows Azure.

Initializing the Table Storage Service
We create a TableStorageService that takes care of connecting and initializing our Cloud Storage Account. It also has a static method that creates the Table if it is not already there.
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
namespace StatsOnAzure.Services
{
public class TableStorageService
{
private static readonly CloudStorageAccount _storageAccount =
CloudStorageAccount.Parse(
CloudConfigurationManager
.GetSetting("TableStorageConnectionString"));
public CloudStorageAccount StorageAccount
{
get { return _storageAccount; }
}
public static void CreateTableIfNotExist(string tableName)
{
CloudTableClient tableClient = _storageAccount.CreateCloudTableClient();
tableClient.CreateTableIfNotExist(tableName);
}
}
}
We call this code only once on start up. In this case from our Global.asax file. We provide our call to CreateTableIfNotExist with the TableName stored in our Sample Data History Repository (read on).
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using StatsOnAzure.App_Start;
using StatsOnAzure.Repositories;
using StatsOnAzure.Services;
namespace StatsOnAzure
{
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
TableStorageService.CreateTableIfNotExist(
SampleDataHistoryRepository.TableName);
}
}
}
Creating the Repository
We build a simple repository for all our Table access. This way we cluster all our data access code in on class, shielding the nitty-gritty details from the rest of our app. This repository does only eager loading.
using System.Collections.Generic;
using System.Data.Services.Client;
using System.Linq;
using Microsoft.WindowsAzure.StorageClient;
using StatsOnAzure.Models;
using StatsOnAzure.Services;
namespace StatsOnAzure.Repositories
{
public class SampleDataHistoryRepository
{
private readonly TableServiceContext _serviceContext;
private readonly CloudTableClient _tableClient;
public SampleDataHistoryRepository(TableStorageService service)
{
_tableClient = service.StorageAccount.CreateCloudTableClient();
_serviceContext = _tableClient.GetDataServiceContext();
}
public static string TableName
{
get { return "StatsOnAzure"; }
}
public static string PartitionKey
{
get { return "SampleDataHistory"; }
}
public DataServiceResponse SaveChanges()
{
return _serviceContext.SaveChangesWithRetries();
}
public void Add(SampleDataHistoryItem item)
{
_serviceContext.AddObject(TableName, item);
}
public void Add(string numbers)
{
var item = new SampleDataHistoryItem {SampleData = numbers};
Add(item);
}
public IEnumerable<SampleDataHistoryItem> GetAll()
{
return _serviceContext
.CreateQuery<SampleDataHistoryItem>(TableName)
.AsTableServiceQuery<SampleDataHistoryItem>()
.Where(e => e.PartitionKey == PartitionKey)
.ToList();
}
public SampleDataHistoryItem GetByRowKey(string key)
{
return GetAll()
.FirstOrDefault(e => e.RowKey == key);
}
public void RemoveByRowKey(string rowKey)
{
SampleDataHistoryItem entity = GetByRowKey(rowKey);
if (entity != null)
{
_serviceContext.DeleteObject(entity);
_serviceContext.SaveChangesWithRetries();
}
}
public IEnumerable<SampleDataHistoryItem> GetLatestSampleDataHistory(int count)
{
return GetAll()
.OrderByDescending(e => e.Timestamp)
.Take(count);
}
public void RemoveObsoleteSampleDataFromHistory(int skipCount)
{
IEnumerable<SampleDataHistoryItem> items = GetAll()
.OrderByDescending(e => e.Timestamp)
.Skip(skipCount);
foreach (SampleDataHistoryItem sampleDataHistoryItem in items)
{
RemoveByRowKey(sampleDataHistoryItem.RowKey);
}
}
}
}
In the constructor we create the actual Data Service Context by calling GetDataServiceContext on the CloudTableClient. This context allows us to perform data operations against the Table service. The rest of the methods make the calls to the Azure Table, like GetAll and GetByRowKey. We first call the ObjectContext CreateQuery with a particular entity type providing the table name. The we call AsTableServiceQuery to convert the DataServiceQuery to a CloudTableQuery. The latter represents an actual query against the Windows Azure Table service.
Extending the Model
Remember, we want to add a list of the ten latest data samples. So we add to the ol’ Number model a SampleDataHistory property to represent that list.
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using StatsOnAzure.Commands;
namespace StatsOnAzure.Models
{
public class NumberModel
{
[Required]
[DataType(DataType.MultilineText)]
[Display(Name = "Population of double values")]
public string Numbers { get; set; }
public double Count { get; set; }
public IEnumerable<ICommand> Statistics { get; set; }
public IEnumerable<SampleDataHistoryItem> SampleDataHistory { get; set; }
}
}
Extending the Controller
Now each time the HomeController gets a request to crunch some sample data we store that data value in our Azure Table Storage. We do this by calling methods on the repository to add, retrieve and delete sample data history items.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using StatsOnAzure.Commands;
using StatsOnAzure.Helpers;
using StatsOnAzure.Models;
using StatsOnAzure.Repositories;
using StatsOnAzure.Services;
namespace StatsOnAzure.Controllers
{
public class HomeController : Controller
{
private const int NumHistoryItems = 10;
[AllowAnonymous]
[HttpGet]
public ActionResult Index(NumberModel model, string rowKey)
{
var repository = new SampleDataHistoryRepository(new TableStorageService());
try
{
SetHomeTitles();
if (rowKey != null)
{
model.Numbers = repository.GetByRowKey(rowKey).SampleData;
repository.RemoveByRowKey(rowKey);
}
if (!string.IsNullOrEmpty(model.Numbers))
{
CrunchNumbers(model);
SetCrunchedTitles();
repository.Add(model.Numbers);
repository.SaveChanges();
}
model.SampleDataHistory = repository.GetLatestSampleDataHistory(NumHistoryItems);
repository.RemoveObsoleteSampleDataFromHistory(NumHistoryItems);
repository.SaveChanges();
}
catch (Exception)
{
model.Numbers = string.Empty;
SetFailedTitles();
}
return View(model);
}
[AllowAnonymous]
[HttpPost]
public ActionResult Index(NumberModel model)
{
var repository = new SampleDataHistoryRepository(new TableStorageService());
try
{
CrunchNumbers(model);
SetCrunchedTitles();
repository.Add(model.Numbers);
repository.SaveChanges();
model.SampleDataHistory = repository.GetLatestSampleDataHistory(NumHistoryItems);
repository.RemoveObsoleteSampleDataFromHistory(NumHistoryItems);
repository.SaveChanges();
}
catch (Exception)
{
model.Numbers = string.Empty;
SetFailedTitles();
}
return View(model);
}
// Crunching numbers and setting titles omitted from listing
}
}
Extending the View
Finally we have to add some Razor markup in our view to render the sample data history list. It is simply a list of action links. If we click on a link the RowKey is sent back to the controller where the history item is retrieved from the Azure Table Storage. Once retrieved it is used to calculate the statistical values the same way we did in our previous example.
@section sampledatahistory
{
@if (Model != null && Model.SampleDataHistory != null)
{
<h4>
The last @Model.SampleDataHistory.Count() pieces of sample data</h4>
foreach (SampleDataHistoryItem sampleDataItem in Model.SampleDataHistory)
{
@Html.ActionLink(
string.Format("{0}", sampleDataItem.SampleData.Substring(
0,
sampleDataItem.SampleData.Length > 32
? 32
: sampleDataItem.SampleData.Length)),
"Index",
"Home",
new {sampleDataItem.RowKey},
null)
<br />
}
}
}
Using Azure Table Storage
Now we have our app working on the local Azure Emulator, it is time to run it against the real thing. We do this by modifying our connectionstring to tell our app to use the Table Storage on Azure. Right click the Web role for our project and choose properties and the settings tab. Choose our connectionstring and enter the storage account credentials by choosing the proper account name and key:

If we hit F5 to debug we’re running against the Azure Table Storage in the cloud. Once we’re done we can publish our app to the staging environment on Windows Azure as described here.