Quantcast
Channel: Brian Pedersen's Sitecore and .NET Blog
Viewing all articles
Browse latest Browse all 286

Azure Table Storage Create Read Update Delete entities – a base class implementation using C#

$
0
0

Azure Table Storage is a simple way of storing structured data with no relations (also known as structured NoSQL data) in a storage account. Think of it as an spreadsheet with 2 mandatory keys (a partition key and a row key).

Simple Table Storage Table
Simple Table Storage Table

The data you insert is called entities, and in C# you implement the ITableEntity interface to create a class that can be inserted into a table.

With a little bit of plumbing code you can create a base class that overcomes the limitations of the Table Storage.

This GIT repository contains a simple base class implementation that makes it easy to do CRUD operations on a Table Storage:

https://github.com/briancaos/azuretablerepository

It simplifies several things:

  • It uses strong types.
  • Read an entire table without paging.
  • No size limit when bulk insert, update, delete.

HOW TO SET UP THE BASE CLASS

STEP 1: Implement a ITableEntity class

This class is the contents of your table. This is an example class:

public class MyEntity : ITableEntity
{
  /// <summary>
  /// Mandatory field (partition id)
  /// </summary>
  public string? PartitionKey { get; set; }
  /// <summary>
  /// Mandatory field (row id)
  /// </summary>
  public string? RowKey { get; set; }
  /// <summary>
  /// Mandatory field (last modified)
  /// </summary>
  public DateTimeOffset? Timestamp { get; set; } = default!;
  /// <summary>
  /// Mandatory field (unique id)
  /// </summary>
  public ETag ETag { get; set; } = default!;

  /// <summary>
  /// Custom table field
  /// </summary>
  public string? Value1 { get; set; } 
}

STEP 2: Create a concrete implementation of the class

In this class you specify the name of the table to store your “MyEntity”. And you give base class the name of the ITableEntity to store in that table.

using Azure.Data.Tables;

namespace MyCode
{
    public class MyRepository : AzureTableBaseRepository<MyEntity>
    {
        public MyRepository(TableServiceClient tableServiceClient) : base("MyTable", tableServiceClient)
        {
        }
    }
}

STEP 3: Setup dependency injection in Program.cs

// Creating a connection to a storage account containing the 
// Azure Table
builder.Services.AddAzureClients(context => context.AddTableServiceClient(builder.Configuration.GetConnectionString("StorageAccount")));
// Allowing for depencency injection
builder.Services.AddTransient<MyRepository>();

HOW TO USE THE REPOSITORY

Injecting class into other class

public class MyClass 
{
    private MyRepository _myRepository;

    public MyClass(MyRepository myRepository)
    {
        _myRepository = myRepository;
    }

    ...
    ...
}

Create, Update, or Upsert 1 entity

var entity = new MyEntity() 
{ 
    PartitionKey = "partition",
    RowKey = "row001",
    Value1 = "myvalue"
};

// Adds a new row. Will fail if the partitionkey/rowkey already exists
await _myRepository.AddAsync(entity);

// Updates a row. Will fail if the partitionkey/rowkey does not exist
await _myRepository.UpdateAsync(entity);

// If entity exists, updates the entity. If not, a new is created
await _myRepository.UpsertAsync(entity);

Handle concurrency issues on update

You can use the UpdateAsync(T entity, ETag ifMatch, TableUpdateMode tableUpdateMode) method to handle concurrency issues:

int maxRetries = 10;
int retryCount = 0;
while (retryCount < maxRetries)
{
    try
    {
        var entity = _myRepository.GetAsync("partition", "row001");
        entity.Value1 = entity.Value1 + "1";
        await _myRepository.UpdateAsync(entity, entity.ETag, TableUpdateMode.Replace);
        break;
    }
    // 412 = Precondition Failed (ETag mismatch)
    catch (RequestFailedException ex) when (ex.Status == 412) 
    {
        retryCount++;
        if (retryCount == maxRetries)
        {
            throw new Exception("Cannot update entity");
        }
        else
        {
            // Exponential backoff (0.5s, 1s, 1.5s, ...)
            await Task.Delay(500 * retryCount);
        }
    }
}

Create, Update, or Upsert many entities

In these methods, you don’t need to worry about the insert limits of Azure Tables, the class will chunk your array for you.

List<MyEntity> entityList = new List<MyEntity>();

for (int i=0;i<100;i++)
{
    var entity = new MyEntity() 
    { 
        PartitionKey = "partition",
        RowKey = "row" + i,
        Value1 = "myvalue"
    };
}

// Adds new rows. Will fail if the partitionkeys/rowkeys already exists
await _myRepository.AddAsync(entityList);

// Updates rows. Will fail if the partitionkeys/rowkeys does not exist
await _myRepository.UpdateAsync(entityList);

// If entities exists, updates the entities. If not, new entities are created
await _myRepository.UpsertAsync(entityList);

Read entities

// One specific entity
var entity = await _myRepository.GetAsync("partition", "row001");

// Using LINQ to query table. Returns all entities matching the 
// query.
var entities = await _myRepository.GetAsync(e => e.PartitionKey == "partition");

That’s it. You are now a Azure Table Storage expert. Happy coding.

MORE TO READ:


Viewing all articles
Browse latest Browse all 286

Trending Articles