Update Expressions
Build type-safe update expressions using lambda expressions for DynamoDB UpdateItem operations.
Overview
Update expressions allow you to modify existing items in DynamoDB. FluentDynamoDb provides a type-safe, expression-based approach that offers compile-time validation, IntelliSense support, and automatic parameter generation.
Benefits
- Type Safety - Compile-time checking of property names and types
- IntelliSense - Full IDE support with autocomplete
- Refactoring - Rename properties safely across your codebase
- Automatic Parameters - No manual parameter binding required
SET Operations
SET operations assign values to attributes. This is the most common update operation.
Simple Value Assignment
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
Name = "John Doe",
Email = "john@example.com",
Status = "active"
})
.UpdateAsync();
Using Variables
var newName = "John Doe";
var timestamp = DateTime.UtcNow;
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
Name = newName,
UpdatedAt = timestamp
})
.UpdateAsync();
Conditional Assignment with if_not_exists
Sets a value only if the attribute doesn't exist:
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
ViewCount = x.ViewCount.IfNotExists(0),
CreatedAt = x.CreatedAt.IfNotExists(DateTime.UtcNow)
})
.UpdateAsync();
ADD Operations
ADD operations atomically increment numbers or add elements to sets.
Atomic Increment
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
LoginCount = x.LoginCount.Add(1),
ViewCount = x.ViewCount.Add(5)
})
.UpdateAsync();
Atomic Decrement
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
Credits = x.Credits.Add(-10) // Subtract 10
})
.UpdateAsync();
Add to Set
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
Tags = x.Tags.Add("premium", "verified")
})
.UpdateAsync();
REMOVE Operations
REMOVE operations delete entire attributes from an item.
Remove Single Attribute
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
TempData = x.TempData.Remove()
})
.UpdateAsync();
Remove Multiple Attributes
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
TempData = x.TempData.Remove(),
CachedValue = x.CachedValue.Remove()
})
.UpdateAsync();
REMOVE deletes the attribute entirely, which is different from setting it to null. Key attributes (partition and sort keys) cannot be removed.
DELETE Operations
DELETE operations remove specific elements from sets (not to be confused with REMOVE which deletes entire attributes).
Delete from String Set
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
Tags = x.Tags.Delete("old-tag", "deprecated")
})
.UpdateAsync();
Delete from Number Set
await table.Update<Product>("prod123")
.Set(x => new ProductUpdateModel
{
CategoryIds = x.CategoryIds.Delete(5, 10)
})
.UpdateAsync();
List Operations
Append to List
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
History = x.History.ListAppend("login", "profile-view")
})
.UpdateAsync();
Prepend to List
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
RecentActivity = x.RecentActivity.ListPrepend("new-event")
})
.UpdateAsync();
Arithmetic Operations
Perform arithmetic directly in SET clauses:
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
Score = x.Score + 10,
Balance = x.Balance - 5.00m
})
.UpdateAsync();
Property-to-Property Arithmetic
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
TotalScore = x.BaseScore + x.BonusScore
})
.UpdateAsync();
Anonymous Types for Multiple Properties
Use anonymous types to update multiple properties in a single expression. This is the recommended pattern for updating several fields at once.
Basic Pattern
await table.Update<User>("user123")
.Set(x => new { Name = "John Doe", Age = 30 })
.UpdateAsync();
With Mixed Operations
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
// SET operations
Name = "John Doe",
Status = "active",
UpdatedAt = DateTime.UtcNow,
// ADD operations
LoginCount = x.LoginCount.Add(1),
Tags = x.Tags.Add("premium"),
// List operations
History = x.History.ListAppend("profile-update"),
// REMOVE operations
TempData = x.TempData.Remove()
})
.UpdateAsync();
Using Variables in Anonymous Types
var newName = "Jane Doe";
var newStatus = "active";
var timestamp = DateTime.UtcNow;
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
Name = newName,
Status = newStatus,
UpdatedAt = timestamp,
LoginCount = x.LoginCount.Add(1)
})
.UpdateAsync();
Conditional Updates
Combine Set() with Where() to perform conditional updates that only succeed when certain conditions are met.
Basic Conditional Update
await table.Update<User>("user123")
.Where(x => x.Status == "active")
.Set(x => new UserUpdateModel { Name = "Jane Doe" })
.UpdateAsync();
Optimistic Locking with Version
Use version checking to prevent concurrent update conflicts:
var currentVersion = 5;
await table.Update<User>("user123")
.Where(x => x.Version == currentVersion)
.Set(x => new UserUpdateModel
{
Name = "Jane Doe",
Version = currentVersion + 1
})
.UpdateAsync();
Only Update If Exists
await table.Update<User>("user123")
.Where(x => x.UserId.AttributeExists())
.Set(x => new UserUpdateModel { Name = "Jane Doe" })
.UpdateAsync();
Complex Conditions
await table.Update<User>("user123")
.Where(x => x.Status == "active" && x.Age >= 18)
.Set(x => new UserUpdateModel
{
Verified = true,
VerifiedAt = DateTime.UtcNow
})
.UpdateAsync();
Transactions with Updates
Updates can be included in transactions for atomic multi-item operations:
await DynamoDbTransactions.Write
.Add(table.Update<Order>("customer123", "ORDER#order456")
.Set(x => new { Status = "shipped" }))
.Add(table.Update<Inventory>("product789")
.Where(x => x.Quantity >= 1)
.Set(x => new { Quantity = x.Quantity.Add(-1) }))
.CommitAsync();
Format String Alternative
For scenarios requiring more control, you can use format strings:
// Format string approach
await table.Update<User>("user123")
.Set($"SET {User.Fields.Name} = {{0}}, {User.Fields.Status} = {{1}}",
"Jane Doe", "active")
.UpdateAsync();
// ADD with format string
await table.Update<User>("user123")
.Set($"ADD {User.Fields.LoginCount} {{0}}", 1)
.UpdateAsync();
// REMOVE with format string
await table.Update<User>("user123")
.Set($"REMOVE {User.Fields.TempField}")
.UpdateAsync();
Best Practices
Use ADD for Counters
// ✅ Recommended: ADD creates attribute if it doesn't exist
LoginCount = x.LoginCount.Add(1)
// ⚠️ Arithmetic requires attribute to exist
LoginCount = x.LoginCount + 1
Combine Operations Efficiently
// ✅ Good: Single update with multiple operations
await table.Update<User>("user123")
.Set(x => new UserUpdateModel
{
Name = "John",
LoginCount = x.LoginCount.Add(1),
TempData = x.TempData.Remove()
})
.UpdateAsync();
// ❌ Avoid: Multiple separate updates
await table.Update<User>("user123")
.Set(x => new UserUpdateModel { Name = "John" })
.UpdateAsync();
await table.Update<User>("user123")
.Set(x => new UserUpdateModel { LoginCount = x.LoginCount.Add(1) })
.UpdateAsync();
Use Optimistic Locking for Concurrent Updates
// ✅ Prevents lost updates in concurrent scenarios
await table.Update<User>("user123")
.Where(x => x.Version == currentVersion)
.Set(x => new UserUpdateModel
{
Data = newData,
Version = currentVersion + 1
})
.UpdateAsync();
Next Steps
- Lambda Expressions - Type-safe query building (recommended)
- Formatted Expressions - Format string syntax
- String Expressions - Manual expression building for explicit control
- Filters - Filter expressions for queries