Using Visual Studio, create a C# Console application from the standard template. By default the created project will not have a reference to the storage library, but we can add that easily using the NuGet package manager.
Right-click on the project and select ‘Manage NuGet Packages…‘ from the context menu.
This will load up the Package Manager UI. Select the ‘Online’ tab from the Package Manager dialog, and search for ‘Azure Storage’. As of the time of this writing version 2.1.0.3 was available. Select the Windows Azure Storage package and click ‘Install’.
The same result can be achieved via the Package Manager Console if you prefer to manage your packages from a command line. Type ‘
Install-Package WindowsAzure.Storage'
into the Package Manager Console and the assemblies will be added to your project just as they would be via the UI show above. You’ll see that several assemblies have been added to the project references including ones for OData, spatial and Entity Data Model (EDM). These are used by the client library when working with table storage.
Open the program.cs file and add the following statements to the top of the file:
You will need to modify the code above and change the value for the
accountName
to match your own storage account name. Then provide one of the account storage keys from the portal to assign to the accountKey
variable.
Walking through the code above you’ll see that we use the
accountName
and accountKey
variables to create aStorageCredential
object. This is then used to create a CloudStorageAccount
object, which is the root object we use for access to any of the storage subsystems: BLOBs, queues and, of this article Tables. You’ll see that we pass in the credentials for the storage account as well as indicate that we want to access to the storage account using HTTPS. When we make a call against table storage from the storage library, the actual call to the service behind the scenes is made against the REST based API. Each call is signed using the credentials and, if we specify to use HTTPS, it is sent encrypted over the wire.
Note that there are other ways to create the
CloudStrorageAccount
object, such as using the staticCloudStorageAccount.Parse
method if you have a full storage account connection string. In your production code you should store the credentials or the connection string in configuration and read the values from there, or have them passed in to your code so that you aren’t hard coding the account to be used.
After the
CloudStorageAccount
is created the code then creates a CloudTableClient
object, which is used as a façade to work with Table Storage directly. The code then creates a CloudTable
object using the GetTableReference
method of theCloudTableClient
object. This is just a reference for the client library to use, it hasn’t made a call to the REST API yet at all. The next line, table.CreateIfNotExists(),
will actually make the first call to the Table service REST API and, if a table named “sportingproducts” doesn’t already exist within the storage account it will create it. Note that the call to CreateIfNotExists
is idempotent, meaning we can call it multiple times and it will only ensure the table is created. If the table already existed no action would be taken and no data that might already exist within the table would be changed.
After the table is created we write to the console the URL of the table. Remember that the table service, like all the Windows Azure Storage services, exists as a REST based API so every table has its own resource location, or URI. Calls against this table, such as inserts, selects, etc., are all sent to this URI.
Now that a table has been created we can add entities to it. First, we define an entity. Add the following class to the project:
You’ll notice that we inherit our entity from
TableEntity
, which is a base class that provides the required properties ofPartitionKey
, RowKey
and Timestamp
. Note that you don’t have to inherit from this base class, but if you choose not you will want to implement the ITableEntity
interface, including implementing some methods that are handled for your on theTableEntity
object. The Table storage classes and method in the storage library assume your entities will either inherit fromTableEntity
or implement the ITableEntity
interface. Choosing to not do either of these is possible, but is beyond the scope of this article.
Every entity stored must have a partition key and row key provided. The first constructor in the example uses a category of sporting goods as the partition key and the product SKU as the row key. The constructor passes those values along to the base
TableEntity
constructor that takes the partition key and row key parameters. Later in the article we will cover more on this choice for our keys and how it affects queries. You will notice that we also define a second constructor that has no parameters. This is required so that the object can be deserialized later when being retrieved from storage by the client library’s default implementation within TableEntity
.
We have defined an entity, so now we can create one and add it to the table. Add the following code immediately after we write out the URI to the console in the
Main
method:
Here the code is creating an instance of the
SportingProductEntity
object providing the category of ‘Baseball’ and a SKU of ‘BBt1032’. A TableOperation
object is created to insert the entity. If you look at the static methods for TableOperation
you’ll see you can perform multiple types of operations such as Delete
, Merge
, InsertOrMerge
, or Replace
. The next line of code executes the command against the table. When this line of code executes behind the scenes, an OData insert
command against the table is generated and sent to the REST based API, which then inserts the row into the table.
With the addition of this code, our method is actually no longer idempotent. If you run this code more than once you’ll receive an exception. Digging down into the exception information, you’ll find the error code “EntityAlreadyExists”. This is because the Insert has already occurred and the unique entity, as defined by the partition key and row key, is already in the table.
Change the creation of the
TableOperation
line to be the following:
Now, when you run the code, you will no longer receive the exception. We have told the system to either insert the entity if it is not present, or to completely replace the entity with this instance if the entity is already in the table. The way that you decide to deal with key collisions in your own solutions will depend on where the data is coming from and your own business requirements. As mentioned earlier in the article, you have a number of table operation options to choose from.