DynamoDb, Reading and Writing Data with .Net Core – Part 1

Full source code available here.

A few weeks ago I started playing with DynamoDb in a .NET application. I read through the AWS documentation but felt it was incomplete and a little out of date. This made it quite hard to figure out the “right” way of using the AWS DynamoDb libraries. For example, there is no good example of using dependency injection to pass a DynamoDb client into a controller, and no guidance on whether that client should be request, transient of singleton scoped.
https://github.com/localstack/awscli-local
In this and following posts I’m going to describe approaches that work, they may not be “right” either.
This post is will show how to read and write from DynamoDb using the document interface and pass in a DynamoDb client to controller

Getting Started

Getting started with AWS can feel like a bit of a hurdle (especially due to credential complications), and a good way to get your feet wet is to use localstack – https://github.com/localstack/localstack, consider installing https://github.com/localstack/awscli-local too.

I’m not going to describe how to get localstack running, I’m going to assume you have done that or you have an AWS account and you know how to set the required credentials.

Once you have localstack installed or you AWS account working, run the following to create the DynamoDB table.

aws --endpoint-url=http://localhost:4569 dynamodb create-table --table-name People  --attribute-definitions AttributeName=PersonId,AttributeType=N --key-schema AttributeName=PersonId,KeyType=HASH --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1

You can add data to the table with the following –

aws --endpoint-url=http://localhost:4569 dynamodb put-item --table-name People  --item '{"PersonId":{"N":"1"},"State":{"S":"MA"}, "FirstName": {"S":"Alice"}, "LastName": {"S":"Andrews"}}'
aws --endpoint-url=http://localhost:4569 dynamodb put-item --table-name People  --item '{"PersonId":{"N":"2"},"State":{"S":"MA"}, "FirstName": {"S":"Ben"}, "LastName": {"S":"Bradley"}}'
aws --endpoint-url=http://localhost:4569 dynamodb put-item --table-name People  --item '{"PersonId":{"N":"3"},"State":{"S":"MA"}, "FirstName": {"S":"Colin"}, "LastName": {"S":"Connor"}}'

To see all the items you just stored –

aws --endpoint-url=http://localhost:4569 dynamodb scan --table-name People

The Web API application

I’m going to show you how to retrieve and item by an id and to store a new item using the document interface, in the next post I’ll show how to do the same with the object interface.

But first things first, configuration.

Open the appsettings.json file and replace what’s there with –

{
  "AWS": {
    "Profile": "localstack-test-profile",
    "Region": "us-east-1",
    "ServiceURL": "http://localhost:4566"
  }
}

Add the AWSSDK.DynamoDBv2 nuget package to the project.

In Startup.cs add a using Amazon.DynamoDBv2;

In ConfigureServices() add the following –

public void ConfigureServices(IServiceCollection services)
{
    var awsOptions = Configuration.GetAWSOptions();

    services.AddDefaultAWSOptions(awsOptions);
    services.AddAWSService<IAmazonDynamoDB>();
    services.AddControllers();
}

Inside the controller I’m going to use an AmazonDynmoDbClient to execute calls against DybamoDb, but the SDK doesn’t seem to provide a way to inject one directly, so I came up with a workaround.

//snip...
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DocumentModel;
//snip.. 

public class PersonController : ControllerBase
{
    private readonly AmazonDynamoDBClient _amazonDynamoDbClient;
    public PersonController(IAmazonDynamoDB amazonDynamoDB)
    {
        _amazonDynamoDbClient = (AmazonDynamoDBClient) amazonDynamoDB;
    }
    //snip..

Line 8 declares an AmazonDynamoDBClient.
Line 9, the constructor takes an IAmazonDynamoDB as a parameter.
Line 11 casts this IAmazonDynamoDB to an AmazonDynamoDBClient.
Again, I don’t know if this is the best approach, but it works fine.

Here is how to get an item from the people table.

[HttpGet("{personId}")]
public async Task<IActionResult> Get(int personId)
{
    Table people = Table.LoadTable(_amazonDynamoDbClient, "People");
    var person =  JsonSerializer.Deserialize<Person> ((await people.GetItemAsync(personId)).ToJson());
    return Ok(person);
}

This is how you add an item to the People table.

[HttpPost]
public async Task<IActionResult> Post(Person person)
{
    Table people = Table.LoadTable(_amazonDynamoDbClient, "People");
    
    var document = new Document();
    document["PersonId"] = person.PersonId;
    document["State"] = person.State;
    document["FirstName"] = person.FirstName;
    document["LastName"] = person.LastName;
    await people.PutItemAsync(document);
    
    return Created("", document.ToJson());
}

I’m not a fan of all the hardcoded attributes for properties of the person, but this is a way of doing it.
Another approach is to use the object model, and I’ll show that in a later blog post.

To exercise the GET method open your browser and go to http://localhost:5000/people/1.

To exercise the POST you can use Fiddler, Curl, Postman or the very nice extension for REST Client extension for Visual Studio Code.

If you are using that extension, here is the request –

POST http://localhost:5000/person HTTP/1.1
content-type: application/json

{
    "personId": 6,
    "state": "MA",
    "firstName": "Frank",
    "lastName" : "Freeley"
}

Full source code available here.