
Learn how to use AWS DynamoDB with C# in .NET. Step-by-step tutorial with code examples, best practices, and serverless tips. Start building today!
If you are building serverless or cloud-native applications, learning how to use AWS DynamoDB with C# is one of the most valuable skills you can add to your .NET toolkit. DynamoDB is Amazon's fully managed NoSQL database that delivers single-digit millisecond latency at any scale, and it pairs perfectly with .NET workloads running on AWS Lambda, ECS, or EC2. In this DynamoDB C# tutorial, you will learn how to connect, model data, and run CRUD operations using the AWS SDK for .NET — plus the best practices and common pitfalls that trip up most developers.
By the end, you will understand not just how to call DynamoDB from C#, but why the API is designed the way it is, so you can make smart decisions in production.
Why Use AWS DynamoDB with C# and .NET?
Relational databases like SQL Server are excellent for normalized, relational data with complex joins. But for high-throughput, horizontally scalable workloads — think user sessions, shopping carts, IoT telemetry, gaming leaderboards, or event logging — a NoSQL database is often a better fit. Here is why DynamoDB shines for serverless .NET applications:
- Fully managed: No servers to patch, no replicas to configure. AWS handles availability and durability across multiple Availability Zones.
- Predictable performance: Single-digit millisecond reads and writes regardless of table size.
- Serverless-friendly: On-demand capacity mode means you pay per request — ideal for spiky AWS Lambda workloads with no idle cost.
- Auto-scaling: DynamoDB scales throughput up and down automatically, so you never provision for peak and pay for it 24/7.
Because DynamoDB charges for read/write capacity rather than CPU time, it complements the per-invocation billing model of serverless functions almost perfectly.
Setting Up the AWS SDK for .NET
To talk to DynamoDB from C#, you need the AWS SDK for .NET. The relevant NuGet package is AWSSDK.DynamoDBv2. Install it via the .NET CLI:
// Run in your terminal
// dotnet add package AWSSDK.DynamoDBv2
using Amazon;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
The SDK resolves credentials automatically using the default credential chain: environment variables, shared ~/.aws/credentials files, or — best of all in production — IAM roles attached to your Lambda function or EC2 instance. Never hard-code AWS access keys in your source code.
Creating the client is straightforward:
// The client picks up credentials from the default chain automatically
var client = new AmazonDynamoDBClient(RegionEndpoint.USEast1);
Three Programming Models You Should Know
One thing that confuses newcomers to AWS DynamoDB in C# is that the SDK exposes three distinct APIs, layered from low-level to high-level:
- Low-level API (
AmazonDynamoDBClient): Full control, but verbose. You work withAttributeValuedictionaries. - Document model (
Table,Document): A middle ground using flexible JSON-like documents. - Object persistence model (
DynamoDBContext): An ORM-style mapper that turns C# classes into DynamoDB items. This is the most productive for most apps.
We will focus on the object persistence model with DynamoDBContext, then show the low-level API for queries that need precision.
Modeling Data: Mapping a C# Class to a DynamoDB Table
DynamoDB tables require a primary key, which is either a single partition key (hash key) or a composite of a partition key plus a sort key (range key). Choosing the right key is the single most important design decision in DynamoDB, because it determines how data is distributed and queried.
Here is a Product class decorated with DynamoDB attributes:
[DynamoDBTable("Products")]
public class Product
{
[DynamoDBHashKey] // Partition key
public string Category { get; set; }
[DynamoDBRangeKey] // Sort key
public string ProductId { get; set; }
[DynamoDBProperty]
public string Name { get; set; }
[DynamoDBProperty]
public decimal Price { get; set; }
[DynamoDBProperty]
public int StockCount { get; set; }
[DynamoDBProperty("tags")] // Custom attribute name
public List Tags { get; set; }
}
With Category as the partition key and ProductId as the sort key, you can efficiently fetch all products in a category, sorted by their ID — without scanning the whole table.
CRUD Operations: A Complete DynamoDB C# Example
Now let's run the four core operations. First, wrap the client in a DynamoDBContext:
var client = new AmazonDynamoDBClient(RegionEndpoint.USEast1);
var context = new DynamoDBContext(client);
Create and Update (SaveAsync)
In DynamoDB, writing an item that already exists overwrites it. The same SaveAsync method handles both insert and update — this is an "upsert" by default.
var product = new Product
{
Category = "Electronics",
ProductId = "PROD-1001",
Name = "Wireless Headphones",
Price = 79.99m,
StockCount = 120,
Tags = new List { "audio", "bluetooth" }
};
await context.SaveAsync(product);
Console.WriteLine("Product saved.");
Read (LoadAsync)
To read a single item, supply the full primary key. Because we have a composite key, you must pass both the partition key and the sort key:
var item = await context.LoadAsync("Electronics", "PROD-1001");
if (item != null)
{
Console.WriteLine($"{item.Name} costs {item.Price:C}");
}
Delete (DeleteAsync)
await context.DeleteAsync("Electronics", "PROD-1001");
Console.WriteLine("Product deleted.");
Query vs. Scan: The Most Important Performance Lesson
This is where many developers learning AWS DynamoDB with C# make costly mistakes. DynamoDB offers two ways to retrieve multiple items, and they are not interchangeable:
- Query — reads items by partition key (and optionally filters on the sort key). It is fast and only reads the items you target.
- Scan — reads every item in the table, then filters. It is slow and expensive on large tables.
Rule of thumb: use Query whenever possible and avoid Scan in production code paths. A Scan on a million-item table consumes read capacity for all million items even if you only want ten of them.
Here is an efficient query that fetches all electronics priced above a threshold using the object persistence model:
var query = context.QueryAsync(
"Electronics", // Partition key value
new DynamoDBOperationConfig
{
QueryFilter = new List
{
new ScanCondition("Price", ScanOperator.GreaterThan, 50.00m)
}
});
var results = await query.GetRemainingAsync();
foreach (var p in results)
{
Console.WriteLine($"{p.Name}: {p.Price:C}");
}
Note that the query targets a single partition ("Electronics") first, so DynamoDB reads only that partition. The price condition is applied as a filter after the key match — filters reduce the data returned, not the data read, so design your keys so the partition itself is selective.
Low-Level Query with Expression Attributes
For maximum control — for example, paginating with LastEvaluatedKey — drop to the low-level API:
var request = new QueryRequest
{
TableName = "Products",
KeyConditionExpression = "Category = :cat AND begins_with(ProductId, :prefix)",
ExpressionAttributeValues = new Dictionary
{
[":cat"] = new AttributeValue { S = "Electronics" },
[":prefix"] = new AttributeValue { S = "PROD-" }
}
};
var response = await client.QueryAsync(request);
Console.WriteLine($"Found {response.Count} items.");
Best Practices for DynamoDB in .NET Applications
Following these best practices will save you money and prevent production headaches:
- Reuse the client.
AmazonDynamoDBClientis thread-safe and expensive to create. Instantiate it once (as a singleton or a static field) and reuse it across requests. In ASP.NET Core, register it with the DI container viaAddAWSService<IAmazonDynamoDB>(). - Use on-demand capacity for unpredictable traffic. It removes the need to forecast throughput and is ideal for serverless apps.
- Design single-table schemas. Advanced DynamoDB users often store multiple entity types in one table using composite keys and Global Secondary Indexes (GSIs) to minimize round trips.
- Use Global Secondary Indexes for alternate access patterns. If you need to query by an attribute that is not your primary key, create a GSI rather than resorting to a Scan.
- Batch your reads and writes. Use
BatchGetandBatchWriteto handle up to 25 writes or 100 reads per call and reduce network overhead. - Always handle
ProvisionedThroughputExceededException. The SDK retries with exponential backoff automatically, but you should still design for throttling under load.
Common Pitfalls to Avoid
- Overusing Scan: The number one cause of high DynamoDB bills. Audit your code for
ScanAsynccalls. - Hot partitions: If one partition key value gets disproportionate traffic (e.g., a single popular category), you create a throughput bottleneck. Distribute access by choosing high-cardinality partition keys.
- Forgetting eventual consistency: By default, reads are eventually consistent and may return slightly stale data. Pass a consistent-read option when you need the latest write.
- Storing huge items: DynamoDB items max out at 400 KB. Store large blobs in Amazon S3 and keep only a reference in DynamoDB.
Local Development with DynamoDB Local
You do not need a live AWS account to develop. DynamoDB Local runs in Docker and lets you test without incurring charges. Just point your client at it:
var config = new AmazonDynamoDBConfig
{
ServiceURL = "http://localhost:8000"
};
var client = new AmazonDynamoDBClient("dummy", "dummy", config);
This makes your unit and integration tests fast, free, and isolated from production data.
Conclusion: Key Takeaways
Using AWS DynamoDB with C# unlocks a fully managed, infinitely scalable NoSQL database that fits serverless .NET applications like a glove. Here are the key takeaways from this tutorial:
- Install
AWSSDK.DynamoDBv2and prefer the high-levelDynamoDBContextfor productive CRUD operations. - Choose your partition and sort keys carefully — they define your access patterns.
- Always favor Query over Scan to keep latency and costs low.
- Reuse a single thread-safe client, use on-demand capacity for spiky workloads, and reach for Global Secondary Indexes instead of scanning.
- Develop and test locally with DynamoDB Local before deploying to the cloud.
With these fundamentals and best practices, you are ready to build fast, cost-effective, serverless .NET applications on DynamoDB. Start small with a single table, measure your read/write capacity, and scale with confidence. Happy coding!
Your go-to resource for C#, .NET, and modern software development. Follow along for daily tutorials, tips, and real-world examples.
Comments
Post a Comment