DEV Community

Cover image for Using the new C# Azure.Data.Tables SDK with Azure Cosmos DB
Will Velida
Will Velida

Posted on

Using the new C# Azure.Data.Tables SDK with Azure Cosmos DB

Last month, the Azure SDK team released a new library for Azure Tables for .NET, Java, JS/TS and Python. This release brings the Table SDK in line with other Azure SDKs and they use the specific Azure Core packages for handling requests, errors and credentials.

Azure Cosmos DB provides a Table API offering that is essentially Azure Table Storage on steroids! If you need a globally distributed table storage service, Azure Cosmos DB should be your go to choice.

If you're making a choice between Azure Cosmos DB Table API and regular Azure Table Storage, I'd recommend reading the following article.

In this article, I'll show you how we can perform simple operations against a Azure Cosmos DB Table API account using the new Azure.Data.Table C# SDK. Specifically, we'll go over:

  • Installing the SDK ๐Ÿ’ป
  • Connecting to our Table Client and Creating a table ๐Ÿ”จ
  • Defining our entity ๐Ÿงพ
  • Adding an entity โž•
  • Performing Transactional Batch Operations ๐Ÿ’ฐ
  • Querying our Table โ“
  • Deleting an entity โŒ

Let's dive into it!

Installing the SDK ๐Ÿ’ป

Installing the SDK is pretty simple. We can do so by running the following dotnet command:

dotnet add package Azure.Data.Tables
Enter fullscreen mode Exit fullscreen mode

If you prefer using a UI to install the NuGet packages, we can do so by right-clicking our C# Project in Visual Studio, click on Manage NuGet packages and search for the Azure.Data.Tables package:

image

Connecting to our Table Client and Creating a table ๐Ÿ”จ

The SDK provides us with two clients to interact with the service. A TableServiceClient is used for interacting with our table at the account lelvel.

We do this for creating tables, setting access policies etc.

We can also use a TableClient. This is used for performing operations on our entities. We can also use the TableClient to create tables like so:

TableClient tableClient = new TableClient(config["StorageConnection"], "Customers");
            await tableClient.CreateIfNotExistsAsync();
Enter fullscreen mode Exit fullscreen mode

To create our Table Client, I'm passing in my storage connection string from Azure and the name of the table I want to interact with. On the following line, we create the table if it doesn't exist.

To get out Storage Connection string, we can do so from our Cosmos DB account under Connection String:

image

When we run this code for the first time, we can see that the table has been created in our Data Explorer:

image

Defining our entity ๐Ÿงพ

In Table Storage, we create entities in our table that require a Partition Key and a Row Key. The combination of these need to be unique within our table.

Entities have a set of properties and strongly-typed entities need to extend from the ITableEntity interface, which expose Partition Key, Row Key, ETag and Timestamp properties. ETag and Timestamp will be generated by Cosmos DB, so we don't need to set these.

For this tutorial, I'm going to use the above mentioned properties along with two string properties (Email and PhoneNumber) to make up a CustomerEntity type.

public class CustomerEntity : ITableEntity
{
    public string PartitionKey { get; set ; }
    public string RowKey { get; set; }
    public string Email { get; set; }
    public string PhoneNumber { get; set; }
    public DateTimeOffset? Timestamp { get; set; }
    public ETag ETag { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Adding an entity โž•

To add a new entity into our table, we need to instantiate it and simply call the .AddEntityAsync() method to insert it:

CustomerEntity customerEntity = new CustomerEntity()
{
     PartitionKey = "Velida",
     RowKey = "Will",
     PhoneNumber = "0123456789",
     Email = "will@test.com"
};

await tableClient.AddEntityAsync(customerEntity);
Enter fullscreen mode Exit fullscreen mode

Heading back into our Customers table in Cosmos DB, we can see that the entity was successfully inserted.

image

Performing Transactional Batch Operations ๐Ÿ’ฐ

The table service allows us to make multiple operations within a single batch request.

Transaction are 'all-or-nothing', meaning that if one operation in our batch fails, they will all fail! Transactions can perform a mixture of create, delete, update and upsert operations.

Just take note that all operations within a transaction needs to target the same partition key.

In the code snippet below, I'm creating a list of CustomerEntity objects that I'm going to insert into my table as a batch create operation.

We then create a new List of type TableTransactionAction and add the list of entities we want to include in the batch operation to it. We then use the .SubmitTransactionAsync() method to submit the batch operation:

string partitionKey = "Velida";
List<CustomerEntity> familyList = new List<CustomerEntity>
{
      new CustomerEntity
      {
            PartitionKey = partitionKey,
            RowKey = "Don",
            PhoneNumber = "0987612345"
      },
      new CustomerEntity
      {
            PartitionKey = partitionKey,
            RowKey = "Jane",
            PhoneNumber = "0987612345"
      },
      new CustomerEntity
      {
            PartitionKey = partitionKey,
            RowKey = "Jan",
            PhoneNumber = "0987601298",
            Email = "jan@test.com"
      }
};

List<TableTransactionAction> addFamilyBatch = new List<TableTransactionAction>();

addFamilyBatch.AddRange(familyList.Select(f => new TableTransactionAction(TableTransactionActionType.Add, f)));

Response<IReadOnlyList<Response>> response = await tableClient.SubmitTransactionAsync(addFamilyBatch);
Enter fullscreen mode Exit fullscreen mode

Heading back into our table, we can see the entities have been successfully inserted into our Table:

image

Querying our Table โ“

We can query our data in Table storage in a couple of ways. The code snippet below uses a OData expression.

Working with OData query filters can be a pain, but the SDK provides a helper library that makes it a bit easier. Using .CreateQueryFilter(), we can write our OData query like so:

Pageable<TableEntity> oDataQueryEntities = tableClient.Query<TableEntity>(filter: TableClient.CreateQueryFilter($"PartitionKey eq {partitionKey}"));

foreach (TableEntity entity in oDataQueryEntities)
{
    Console.WriteLine($"{entity.GetString("PartitionKey")}:{entity.GetString("RowKey")}");
}
Enter fullscreen mode Exit fullscreen mode

Running this code, we can see a concatenation of our entities PartitionKeys and RowKeys like so:

image

We can also use LINQ expressions to query our Table. Here, I'm using a LINQ query to retrieve a CustomerEntity with a RowKey value of "Will":

Pageable<CustomerEntity> linqEntities = tableClient.Query<CustomerEntity>(customer => customer.RowKey == "Will");

foreach (var entity in linqEntities)
{
     Console.WriteLine($"{entity.RowKey} {entity.PartitionKey}");
}
Enter fullscreen mode Exit fullscreen mode

When this code runs, we can see our entity printed out to us:

image

Deleting an entity โŒ

Deleting entities from our table is just a simple .DeleteEntityAsync() call. All we need to do is pass in our ParitionKey and RowKey value like so:

await tableClient.DeleteEntityAsync(partitionKey, "Will");
Enter fullscreen mode Exit fullscreen mode

Checking our table, we can see that our entity has been successfully deleted:

image

Wrapping up

Hopefully after reading this article, you understand that working with the Azure.Data.Tables SDK is pretty straightforward. I like the approach that the Azure SDK team is taking with making it's SDKs more consistent with each other.

Using the Azure.Data.Tables SDK, we can build applications that work with both Azure Cosmos DB table storage and regular table storage, so if you find that you're building an application using regular Table Storage and you're struggling with scale, you can switch to Cosmos DB without any code changes!

If you want to learn more about the Azure.Data.Tables library, check out this blog post and this GitHub repo!

Hopefully you found this article useful! As always, if you have any questions, feel free to comment below or ask me on Twitter!

Happy Coding! ๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป๐Ÿ‘ฉโ€๐Ÿ’ป

Top comments (0)