An important part of creating APIs is interacting with data storage. While you can use any number of database programs, a common way to store simple data is in data tables. This is particularly useful when you are recording API calls as part of your telemetry.
Storage tables are not inherently relational. The goal is to keep storage tables simple. This makes them ideal for keeping lists, logging, creating progress entries, and so forth.In this demo, we’ll create a storage table that tracks exceptions thrown during execution of our program. Let’s create a console app that just throws exceptions every few seconds.
Create a new console app and name it TableStorageConsoleApp. Choose .NET 8 (or 9) and check Do not use top-level statements.
In main, create a forever loop and have it throw an exception. We want the exceptions to be thrown randomly.
I’m having trouble with WordPress layout, so please forgive the fonts in this post. I’ll sort it out ASAP.
try
{
Random rand = new Random();
var random = rand.Next(0, 10);
switch (random)
{
case 0:
throw new ArgumentException("Argument Exception");
case 1:
throw new ArgumentNullException("Argument Null
Exception");
case 2:
throw new ArgumentOutOfRangeException("Argument Out Of
Range Exception");
case 3:
throw new DivideByZeroException("Divide By Zero
Exception");
case 4:
throw new FileNotFoundException("File Not Found
Exception");
case 5:
throw new FormatException("Format Exception");
case 6:
throw new IndexOutOfRangeException("Index Out Of Range
Exception");
case 7:
throw new InvalidOperationException("Invalid Operation
Exception");
case 8:
throw new KeyNotFoundException("Key Not Found Exception");
case 9:
throw new NotImplementedException("Not Implemented
Exception");
case 10:
throw new NotSupportedException("Not Supported
Exception");
default:
throw new Exception("Generic Exception - you should never
see this");
}
}
Now we can catch each exception and put it into our table.
The key thing here is that the table has Partition keys and Row Keys. Partition keys aggregate rows, and makes retrieval much faster.
TableServiceClient is a NuGet package. We call Upsert and sleep for two seconds.
The table model itself looks like this:
public class TableModel : ITableEntity
{
required public string PartitionKey { get; set; }
required public string RowKey { get; set; }
public DateTimeOffset? Timestamp { get; set; }
public string? Message { get; set; }
public ETag ETag { get; set; } = ETag.All;
}
(Etag is used for optimist concurrency and must be in every table model.
Here is UpsertEntityAsync
public async Task UpsertEntityAsync(TableModel entity){var response = await _tableCreationTask;var table = _tableServiceClient.GetTableClient(response.Value.Name);return await table.UpsertEntityAsync(entity);}
To get started, we declare two member variables at the top of the class:
private readonly TableServiceClient _tableServiceClient;
private readonly Task> _tableCreationTask;
Notice that the second member is of type Task. Both Response andTableItem are supplied by the Azure NuGet package. The constructor takes TableServiceClient
public StorageTableService(TableServiceClient tableServiceClient){_tableServiceClient = tableServiceClient;_tableCreationTask = _tableServiceClient.CreateTableIfNotExistsAsync(“ExceptionsTable”);}
It is here that we create the table if it doesn’t exist and name it ExceptionsTable.All the Upsert method needs to do is call tableCreationTask and wait to get back Response. With that in hand, it is ready to call GetTableClient onTableServiceClient, passing in the name of the table. Finally, we call UpsertEntityAsync on the table passing in TableModel.
Your output should be a simple table with one row for each exception thrown.The next step is to migrate this to Azure which we will do in the next blog post.
Note, this post is based on my forthcoming book Programming APIs with C# and .NET from Packt. Which will be available the end of November 2024.
Pingback: Dew Drop – November 18, 2024 (#4303) – Morning Dew by Alvin Ashcraft