Tutorial: DynamoDB with C# in .NET Core

I’ve been playing around with Alexa apps in .NET Core and trying to pull together a good example of a .NET driven audio skill. This led me to pulling apart the Alexa audio sample & trying to replicate it in C#. But I hit a snag when I realized that, due to the nature of Alexa audio skills, I needed to store the state of the audio player in a persistent way.

In the node.js sample, they use Amazon’s DynamoDB to do this, so I decided to use the same. The model for the object I push into the database is the same model the audio sample uses.

Check out the full source at my github but I’ll just walk through a couple key points.

Nuget Packages

Add the following namespaces

using Amazon;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;

Set Your AWS Credentials

You’ll need your AWS account access key and secret key (I walk through getting those over here… scan down to “Add User”) and I’m using the USEast1 region b/c that’s the region that hosts Alexa skills.

var credentials = new BasicAWSCredentials(accessKey, secretKey);
var client = new AmazonDynamoDBClient(credentials, RegionEndpoint.USEast1);

Verify Your Table

We can get a list of all our tables in that region & look for our table name in that list to verify if the table exists.

var tableResponse = await client.ListTablesAsync();
if (!tableResponse.TableNames.Contains(tableName))
{
    // Create our table if it doesn't exist
}

Create a New Table

To create our table, we need to give it a name, provision the throughput, and give a key (or a set of keys).

await client.CreateTableAsync(new CreateTableRequest
{
    TableName = tableName,
    ProvisionedThroughput = new ProvisionedThroughput
    {
        ReadCapacityUnits = 3,
        WriteCapacityUnits = 1
    },
    KeySchema = new List<KeySchemaElement>
    {
        new KeySchemaElement
        {
            AttributeName = hashKey,
            KeyType = KeyType.HASH
        }
    },
    AttributeDefinitions = new List<AttributeDefinition>
    {
        new AttributeDefinition {
            AttributeName = hashKey,
            AttributeType =ScalarAttributeType.S
        }
    }
});

Wait for Table to Create / Become Active

Because we’re going to immediately start using the table to store data, we need to wait for our table creation to be complete and our table to be active. So I’ve added the following to ensure that we don’t move forward until that happens.

bool isTableAvailable = false;
while (!isTableAvailable) {
    Thread.Sleep(5000);
    var tableStatus = await client.DescribeTableAsync(tableName);
    isTableAvailable = tableStatus.Table.TableStatus == "ACTIVE";
}

Set your DynamoDBContext

With out table verified, we can set our context

var context = new DynamoDBContext(client);

Save Something

In the sample, I create the object inline (I’ll spare you the details of the AlexaAudioState object here) and add it into our database.

await context.SaveAsync<AlexaAudioState>(currentState);

Get Something

To retrieve the object, we use a list of ScanConditions. This was a little new to me (I’m used to using LINQ or plain old SQL queries to retrieve entries from a database) but it seemed straightforward enough to generate the conditions (especially for a retrieving a query based on a key).

List<ScanCondition> conditions = new List<ScanCondition>();
conditions.Add(new ScanCondition("UserId", ScanOperator.Equal, currentState.UserId));
var allDocs = await context.ScanAsync<AlexaAudioState>(conditions).GetRemainingAsync();
var savedState = allDocs.FirstOrDefault();

Delete Your Table

This is commented out in my code, but we can also delete the table by disposing our context and… um… deleting the table.

context.Dispose();
await client.DeleteTableAsync(new DeleteTableRequest() { TableName = tableName });

And that’s it! Our output shows that everything is working.

We can log into our AWS console and go to our DynamoDB console and verify the table.

Clicking on our entry, we can see the detail and edit it if we so choose.