DynamoDB Batch Operations in C#
Perform efficient bulk operations on DynamoDB using FluentDynamoDB. Batch operations allow you to read or write multiple items in a single request, significantly reducing API calls and improving throughput. This guide demonstrates batch writes and batch gets using the three API patterns.
- Lambda/Fluent
- String Formatted
- Manual Builder
Batch Write Operations
// Put multiple items in a single batch
await DynamoDbBatch.Write
.Add(userTable.Put(new User { UserId = "user1", Name = "Alice" }))
.Add(userTable.Put(new User { UserId = "user2", Name = "Bob" }))
.Add(userTable.Put(new User { UserId = "user3", Name = "Charlie" }))
.ExecuteAsync();
Mixed Put and Delete
// Combine put and delete operations
await DynamoDbBatch.Write
.Add(userTable.Put(newUser))
.Add(userTable.Delete("oldUserId"))
.Add(orderTable.Put(newOrder))
.Add(orderTable.Delete("customerId", "oldOrderId"))
.ExecuteAsync();
Multi-Table Batch Write
// Write to multiple tables in one batch
await DynamoDbBatch.Write
.Add(userTable.Put(user))
.Add(orderTable.Put(order))
.Add(auditTable.Put(auditEntry))
.ExecuteAsync();
Batch Get Operations
// Get multiple items with tuple destructuring
var (user, order) = await DynamoDbBatch.Get
.Add(userTable.Get(userId))
.Add(orderTable.Get(orderId))
.ExecuteAndMapAsync<User, Order>();
Console.WriteLine($"User: {user?.Name}");
Console.WriteLine($"Order: {order?.Status}");
Batch Get with Projection
// Retrieve only specific attributes
var (user1, user2, user3) = await DynamoDbBatch.Get
.Add(userTable.Get("user1").WithProjection(x => new { x.Name, x.Email }))
.Add(userTable.Get("user2").WithProjection(x => new { x.Name, x.Email }))
.Add(userTable.Get("user3").WithProjection(x => new { x.Name, x.Email }))
.ExecuteAndMapAsync<User, User, User>();
Batch Get with Index Access
// Access results by index for larger batches
var response = await DynamoDbBatch.Get
.Add(userTable.Get("user1"))
.Add(userTable.Get("user2"))
.Add(userTable.Get("user3"))
.Add(orderTable.Get("order1"))
.ExecuteAsync();
var user1 = response.GetItem<User>(0);
var user2 = response.GetItem<User>(1);
var user3 = response.GetItem<User>(2);
var order = response.GetItem<Order>(3);
Batch Write Operations
// Put multiple items in a single batch
await DynamoDbBatch.Write
.Add(userTable.Put(new User { UserId = "user1", Name = "Alice" }))
.Add(userTable.Put(new User { UserId = "user2", Name = "Bob" }))
.Add(userTable.Put(new User { UserId = "user3", Name = "Charlie" }))
.ExecuteAsync();
Mixed Put and Delete
// Combine put and delete operations
await DynamoDbBatch.Write
.Add(userTable.Put(newUser))
.Add(userTable.Delete("oldUserId"))
.Add(orderTable.Put(newOrder))
.Add(orderTable.Delete("customerId", "oldOrderId"))
.ExecuteAsync();
Multi-Table Batch Write
// Write to multiple tables in one batch
await DynamoDbBatch.Write
.Add(userTable.Put(user))
.Add(orderTable.Put(order))
.Add(auditTable.Put(auditEntry))
.ExecuteAsync();
Batch Get Operations
// Get multiple items with tuple destructuring
var (user, order) = await DynamoDbBatch.Get
.Add(userTable.Get(userId))
.Add(orderTable.Get(orderId))
.ExecuteAndMapAsync<User, Order>();
Console.WriteLine($"User: {user?.Name}");
Console.WriteLine($"Order: {order?.Status}");
Batch Get with Projection
// Retrieve only specific attributes using string projection
var (user1, user2, user3) = await DynamoDbBatch.Get
.Add(userTable.Get("user1").WithProjection("name, email"))
.Add(userTable.Get("user2").WithProjection("name, email"))
.Add(userTable.Get("user3").WithProjection("name, email"))
.ExecuteAndMapAsync<User, User, User>();
Batch Get with Index Access
// Access results by index for larger batches
var response = await DynamoDbBatch.Get
.Add(userTable.Get("user1"))
.Add(userTable.Get("user2"))
.Add(userTable.Get("user3"))
.Add(orderTable.Get("order1"))
.ExecuteAsync();
var user1 = response.GetItem<User>(0);
var user2 = response.GetItem<User>(1);
var user3 = response.GetItem<User>(2);
var order = response.GetItem<Order>(3);
Batch Write Operations
// Put multiple items in a single batch
await DynamoDbBatch.Write
.Add(userTable.Put(new User { UserId = "user1", Name = "Alice" }))
.Add(userTable.Put(new User { UserId = "user2", Name = "Bob" }))
.Add(userTable.Put(new User { UserId = "user3", Name = "Charlie" }))
.ExecuteAsync();
Mixed Put and Delete
// Combine put and delete operations
await DynamoDbBatch.Write
.Add(userTable.Put(newUser))
.Add(userTable.Delete("oldUserId"))
.Add(orderTable.Put(newOrder))
.Add(orderTable.Delete("customerId", "oldOrderId"))
.ExecuteAsync();
Multi-Table Batch Write
// Write to multiple tables in one batch
await DynamoDbBatch.Write
.Add(userTable.Put(user))
.Add(orderTable.Put(order))
.Add(auditTable.Put(auditEntry))
.ExecuteAsync();
Batch Get Operations
// Get multiple items with tuple destructuring
var (user, order) = await DynamoDbBatch.Get
.Add(userTable.Get(userId))
.Add(orderTable.Get(orderId))
.ExecuteAndMapAsync<User, Order>();
Console.WriteLine($"User: {user?.Name}");
Console.WriteLine($"Order: {order?.Status}");
Batch Get with Projection
// Retrieve only specific attributes using manual projection
var (user1, user2, user3) = await DynamoDbBatch.Get
.Add(userTable.Get("user1").WithProjection("#n, #e")
.WithAttribute("#n", "name")
.WithAttribute("#e", "email"))
.Add(userTable.Get("user2").WithProjection("#n, #e")
.WithAttribute("#n", "name")
.WithAttribute("#e", "email"))
.Add(userTable.Get("user3").WithProjection("#n, #e")
.WithAttribute("#n", "name")
.WithAttribute("#e", "email"))
.ExecuteAndMapAsync<User, User, User>();
Batch Get with Index Access
// Access results by index for larger batches
var response = await DynamoDbBatch.Get
.Add(userTable.Get("user1"))
.Add(userTable.Get("user2"))
.Add(userTable.Get("user3"))
.Add(orderTable.Get("order1"))
.ExecuteAsync();
var user1 = response.GetItem<User>(0);
var user2 = response.GetItem<User>(1);
var user3 = response.GetItem<User>(2);
var order = response.GetItem<Order>(3);
Result Access Patterns
FluentDynamoDB provides multiple ways to access batch get results:
var response = await DynamoDbBatch.Get
.Add(userTable.Get("user1"))
.Add(userTable.Get("user2"))
.Add(userTable.Get("user3"))
.Add(orderTable.Get("order1"))
.ExecuteAsync();
// Single item by index
var user1 = response.GetItem<User>(0);
// Multiple items by indices
var users = response.GetItems<User>(0, 1, 2);
// Contiguous range of items
var allUsers = response.GetItemsRange<User>(0, 2);
// Check total count
Console.WriteLine($"Retrieved {response.Count} items");
Handling Unprocessed Items
DynamoDB may not process all items due to capacity limits. Always check for unprocessed items:
// Batch write with unprocessed item check
var writeResponse = await DynamoDbBatch.Write
.Add(userTable.Put(user1))
.Add(userTable.Put(user2))
.ExecuteAsync();
if (writeResponse.UnprocessedItems.Count > 0)
{
Console.WriteLine("Some items were not processed - implement retry logic");
}
// Batch get with unprocessed key check
var getResponse = await DynamoDbBatch.Get
.Add(userTable.Get("user1"))
.Add(userTable.Get("user2"))
.ExecuteAsync();
if (getResponse.HasUnprocessedKeys)
{
Console.WriteLine("Some keys were not processed - implement retry logic");
}
Batch Limits
| Operation | Limit |
|---|---|
| BatchWriteItem | 25 items per request |
| BatchGetItem | 100 items or 16MB per request |
For larger datasets, chunk your requests:
// Process items in chunks of 25
foreach (var chunk in users.Chunk(25))
{
var batch = DynamoDbBatch.Write;
foreach (var user in chunk)
{
batch.Add(userTable.Put(user));
}
await batch.ExecuteAsync();
}
Learn More
- Batch Operations Guide - Complete batch operations reference with retry patterns
- Basic Operations - Single-item CRUD operations
- Transactions - Atomic operations with ACID guarantees
- Query & Scan - Querying multiple items