LazyMagic.Service.DynamoDBRepo: High-level DynamoDB abstraction library implementing repository pattern for CRUD operations. Features single-table design, envelope pattern, secondary indices (SK1-SK5), optimistic locking, multi-tenancy, soft delete with TTL, and notification system for AWS DynamoDB.
LazyMagic.Service.DynamoDBRepo provides a high-level abstraction for Amazon DynamoDB operations, implementing a repository pattern for CRUDL (Create, Read, Update, Delete, List) operations. It follows DynamoDB best practices with single-table design, supporting multi-tenancy, optimistic locking, notifications, and flexible querying with secondary indices.
IDYDBRepository<T>
IDocumentRepo<T>
IItem
DYDBRepository<T>
PartialContentObjectResult<T>
QueryHelper
public class CustomerRepository : DYDBRepository<Customer>
{
public CustomerRepository(IAmazonDynamoDB dynamoDbClient)
: base(dynamoDbClient)
{
}
protected override void AssignEntityAttributes(
Customer item,
Dictionary<string, AttributeValue> attributes)
{
// Assign secondary indices
attributes["SK1"] = new AttributeValue { S = item.Email };
attributes["SK2"] = new AttributeValue { S = item.City };
}
protected override List<string> AssignTopics(Customer item)
{
return new List<string> { $"customer:{item.Id}" };
}
}
// Create
var result = await repository.CreateAsync(callerInfo, customer);
// Read
var result = await repository.ReadAsync(callerInfo, "Customer:", "123:");
// Update with optimistic locking
var result = await repository.UpdateAsync(callerInfo, customer);
// Delete (soft delete if configured)
var result = await repository.DeleteAsync(callerInfo, "Customer:", "123:");
// Query by email (using SK1 index)
var queryRequest = new QueryRequest
{
IndexName = "SK1",
IndexValue = "user@example.com",
QueryOperator = "Equals"
};
var results = await repository.ListAsync(callerInfo, queryRequest);
// Range query
var queryRequest = new QueryRequest
{
IndexName = "SK2",
IndexValue = "2023-01-01",
IndexValue2 = "2023-12-31",
QueryOperator = "Between"
};
var results = await repository.ListAsync(callerInfo, queryRequest);
// Update will fail if record was modified since last read
var result = await repository.UpdateAsync(callerInfo, customer);
if (result is ConflictObjectResult)
{
// Handle concurrent modification
}
// Force update bypasses optimistic locking
var result = await repository.UpdateAsync(callerInfo, customer, force: true);
public class MyRepository : DYDBRepository<MyEntity>
{
public MyRepository()
{
UseSoftDelete = true;
SoftDeleteTTLDays = 30; // Keep for 30 days
}
}
var callerInfo = new CallerInfo
{
TenantId = "tenant-123",
TableLevel = TableLevel.Tenant // Use tenant-specific table
};
protected override List<string> AssignTopics(Order order)
{
return new List<string>
{
$"order:{order.Id}",
$"customer:{order.CustomerId}"
};
}
protected override async Task WriteNotificationAsync(
ICallerInfo callerInfo,
LzNotification notification)
{
// Custom notification implementation
await NotificationService.PublishAsync(notification);
}
The repository returns ASP.NET Core ActionResults:
Tables follow the naming convention: {prefix}-{entityType}
system-{entityType}
{tenantId}-{entityType}
local-{entityType}
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"dynamodb:PutItem",
"dynamodb:GetItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:Query",
"dynamodb:Scan"
],
"Resource": "arn:aws:dynamodb:*:*:table/*"
}]
}