DEV Community

Chris Perez
Chris Perez

Posted on • Edited on

DynamoSharp: Simplify DynamoDB and Single-Table Design in C# with this ORM [Part 1]

Amazon DynamoDB is a fast and scalable NoSQL database that requires a different approach than traditional relational design. One of the most recommended patterns for working efficiently with DynamoDB is Single-Table Design (STD), which stores multiple entities in a single table to optimize queries and reduce read costs.

In this article, I’ll briefly explain what Single-Table Design is, how to implement it easily with DynamoSharp (a C# ORM), and show a practical example of a one-to-many relationship.


What is Single-Table Design?

Unlike relational databases that normalize data across multiple tables, Single-Table Design in DynamoDB stores all entities in one table. It intelligently uses partition keys, sort keys (PartitionKey and SortKey), and global secondary indexes (GSI).

This approach models relationships between entities (like one-to-many or many-to-many) using well-defined access patterns, resulting in fast and efficient operations.


What is DynamoSharp?

DynamoSharp is an ORM (Object-Relational Mapping) for C# that simplifies using DynamoDB with Single-Table Design. Key features include:

  • Custom mapping for primary/secondary keys.
  • Support for entity relationships (one-to-many, many-to-many).
  • Automatic key generation (convention-based or configurable).
  • Optimistic locking for version control.
  • Retry strategies for DynamoDB errors.
  • Automatic change tracking for entities.

DynamoSharp lets you focus on business logic without worrying about DynamoDB’s underlying complexity.


Example: One-to-Many Relationship

Let’s explore an example where an Order has many Items (a one-to-many relationship).

Installation

dotnet add package DynamoSharp
Enter fullscreen mode Exit fullscreen mode

Configuration

builder.Services.AddDynamoSharp(RegionEndpoint.USEast1);
builder.Services.AddDynamoSharpContext<OrganizationContext>(
    new TableSchema.Builder()
        .WithTableName("dynamosharp")
        .Build()
);
Enter fullscreen mode Exit fullscreen mode

Entities

public class Item
{
    public int Id { get; set; }
    public string ProductName { get; set; }
    public decimal UnitPrice { get; set; }
    public int Units { get; set; }

    ...
}

public class Order
{
    public int Id { get; set; }
    public int BuyerId { get; set; }
    public Address Address { get; set; }
    public Status Status { get; set; }
    public DateTime Date { get; set; }

    private readonly List<Item> _items = new List<Item>();
    public IReadOnlyCollection<Item> Items => _items;

    ...
}
Enter fullscreen mode Exit fullscreen mode

Context Setup

public class EcommerceContext : DynamoSharpContext
{
    public IDynamoDbSet<Order> Orders { get; private set; } = null!;

    public AppContext(IDynamoDbContextAdapter dynamoDbContextAdapter, TableSchema tableSchema) : base (dynamoDbContextAdapter, tableSchema)
    {
    }

    public override void OnModelCreating(IModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Order>()
            .HasOneToMany(o => o.Items);
    }
}
Enter fullscreen mode Exit fullscreen mode

Save

var order = new Order.Builder()
     .WithId(1)
     .WithBuyerId(5)
     .WithAddress("Street 1", "City 1", "State 1", "ZipCode 1")
     .WithStatus(Status.Pending)
     .WithDate(DateTime.Now)
     .Build();

order.AddProduct(2, "Product 1", 26, 93);

ecommerceContext.Orders.Add(order);
ecommerceContext.TransactWriter.SaveChangesAsync(cancellationToken);
Enter fullscreen mode Exit fullscreen mode

Result in DynamoDB

PartitionKey SortKey Id BuyerId ProductName UnitPrice Units
ORDER#1 ORDER#1 1 5
ORDER#1 ITEM#2 2 Product 1 26.0 93

As shown, both the order and its items share the same partition key (ORDER#1). This allows querying the entire order and its items in a single read operation.

Querying an Order

var order = ecommerceContext.Query<Order>()
    .PartitionKey("ORDER#1")
    .ToEntityAsync(cancellationToken);
Enter fullscreen mode Exit fullscreen mode

Example on GitHub

OneToManyExample


Conclusion

Single-Table Design may seem intimidating at first, but tools like DynamoSharp make it accessible for C# developers. This library abstracts DynamoDB’s complexity so you can focus on your models and relationships while maintaining optimal performance and clean code.

Want to learn more? Visit the official GitHub repository:

👉 https://github.com/ChrixApp/DynamoSharp

Top comments (0)