tldr; If you want to cut through the chase and just see the code, you can go directly to the "Making a query" section.
DynamoDB is a NoSQL database that exists in AWS. It's a highly scalable database which can handle loads of traffic. It looks really simple to get started with - actually it's so simple that you do not even have to create a "database" in that sense, everything is abstracted away and handled for you. The only thing you must create is a table, and you're set.
Once your table is created you can start write code to insert and query items. The low-level SDK for .NET Core is surprisingly hard to understand when you first look at it, and that's the reason why I wanted to write this post.
We'll take it step by step, keeping focus on the query itself but also doing all other necessary steps to get there. Before we can make a query we of course need the table itself and something inside to query.
Creating the table
First head into the AWS portal and create a table, I called mine customertestjim. I named my partition key CustomerId and kept the rest as default settings, you can do the same. You should now see your table in the portal with 0 items in it:
Inserting an item
I just manually inserted an item through the AWS portal. Once that is done, all I have in my table is one record with two attributes, CustomerId and Name:
Setup of project
We will be using a simple console application for this purpose, but of course the code I'll show you will work very similar if you were executing it from an AWS Lambda or an ASP.NET Core application. The rest of the post will assume you have created your project, I created mine through dotnet new console. Your .csproj shoud look something like this:
There is really only one thing you need to do before we'll look at the code and are ready to execute it, and that is to get credentials from AWS to authenticate to your DynamoDB. I am doing it by copy-past'ing credentials into the ~/.aws/credentials file (similar on Windows). The SDK does indeed support to enter the credentials through code as well of course, so feel free to do that if you want.
Making a query
I am not using a main method, since that requirement was removed in C# 9. It looks very unfamiliar if you've been doing C# for some years, but this is how you can write your C# code nowadays if you feel you don't need the boilerplate with a namespace and a main method. I've also omitted the braces for the using statement of the client, since that's not needed since C#8.
The table exists and has an item, we do have a project so let's see the code and make a query!
Here's our Program.cs file (we will go through it step by step):
Here's what the code does:
This will instantiate a client we can reuse to issue a couple of operations towards DynamoDB in the specified region. Disposal of the client will happen at the end of the scope. Next is the query part:
Here's the tricky and interesting part. The SDK has a specific syntax that you need to follow to structure your query correctly. We're using three properties here so let's take them one by one:
Name of the table
KeyConditionExpression is a string that must match one partition key and (optionally) also a sort key (read more here). Remember when we created the table we named our partition key to be CustomerId. In this case we're just interesting in the customer with a specific CustomerId, and we specify this by CustomerId = :Id. Since CustomerId is not a reserved name in DynamoDB this is fine, but if we would have sticked to the default name PartitionKey (which is a reserved word) then we would have needed to type it as #PartitionKey = :Id with the # being the important bit here. At a first glance this is really confusing, but that's how it is.
But what about :Id? How do we get our actual id of the customer in here? In order for that we must set some other values, enter ExpressionAttributeValues.
This is a dictionary where the name of the key must match whatever specified in KeyConditionExpression. We do that by passing an entry with :Id in our example and the value is of type AttributeValue with a string value S = "12345" (it's very important to you use S here!). If this column would have been of type number then you'd have to instead use N = "12345" (still passing a string, but you're telling DynamoDB to treat it as a number).
So now we have our query. Next line:
This will send the query and await a response. Do note that we could get multiple hits (technically speaking) for this same CustomerId, since it's really just a PartitionKey, so we will have to call Single to get just one row (since we know there is just one customer in our table).
Here we're just parsing the row and creating a new anonymous object. Since row is of type Dictionary<string, AttributeValue> we must index it with our column name CustomerId and Name. But that's not enough - we must also tell which type we expect the data to be in, and we know it's string for both so we have to add .S to it. Although it looks weird, its hopefully more familiar now since we saw the same thing when creating the query. And that's about it. You can of course create a more complex query including the SortKey or add a ProjectionExpression if you only want to retrieve a certain set of attributes for your customer.
DynamoDB is an extremely powerful and highly scalable database solution. It's surpsingly well abstracted away when you create it - you don't even need to create "a database", just create the table and you're good to go.
The low-level SDK query langauge however is really weird when you first look at it. There is of course many more parameters you can pass if you will, but I wanted to keep it to a minimum just to show you how a "simple" query is made using the low-level SDK in .NET Core 6.