README.md

February 5, 2026 · View on GitHub

DynamoSharp logo

Build Passing Test Passing Coverage NuGet NuGet Downloads License

DynamoSharp is an ORM (Object-Relational Mapping) library for .NET that simplifies interaction with Amazon DynamoDB, with a focus on Single-Table Design. It offers a simple and efficient way to map data models to DynamoDB tables and perform CRUD (Create, Read, Update, Delete) operations intuitively, as well as tracking changes in entities.

Features:

  • Model Mapping: Support for defining primary and secondary keys, global indexes, and relationships between entities.

  • Efficient Queries: Provides a query builder that allows complex searches using partition and sort keys, as well as secondary indexes.

  • Relationship Support: Handles one-to-many and many-to-many relationships between entities.

  • Focus on Single Table Design: Optimizes the use of DynamoDB through single table design, allowing multiple entity types to be stored in a single table and enabling efficient queries.

  • Entity Tracking: Automatically tracks changes in entities to help detect and manage data updates effectively.

  • Version control with Optimistic Locking: Makes sure that updates do not overwrite each other by checking the version of the data before saving changes.

  • Retry Strategies: Automatically handles retries for transient DynamoDB errors such as InternalServerError, LimitExceeded, ProvisionedThroughputExceeded, RequestLimitExceeded, ServiceUnavailable, and Throttling. This feature ensures reliability and removes the need to implement retry logic in your business layer.

DynamoSharp simplifies the use of DynamoDB in .NET applications, providing an abstraction layer that allows developers to focus on business logic without worrying about the details of database interaction, while taking advantage of the benefits of single table design to optimize performance and data efficiency.

Table of Contents

Quick start

Installation

dotnet add package DynamoSharp

Configuration

builder.Services.AddDynamoSharp(RegionEndpoint.USEast1);
builder.Services.AddDynamoSharpContext<AppContext>(
    new TableSchema.Builder()
        .WithTableName("dynamosharp")
        .WithPartitionKeyName("PK")
        .WithSortKeyName("SK")
        .AddGlobalSecondaryIndex("GSI1PK-GSI1SK-index", "GSI1PK", "GSI1SK")
        .AddGlobalSecondaryIndex("GSI2PK-GSI2SK-index", "GSI2PK", "GSI2SK")
        .Build()
);

Connect to DynamoDB Local

builder.Services.AddDynamoSharp(RegionEndpoint.USEast1, "http://localhost:4566");

Implementation

public class ModelItem 
{
    public Guid Id { get; set; }
}

public class AppContext : DynamoSharpContext
{
    public IDynamoDbSet<ModelItem> Items { get; private set; } = null!;

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

    public override void OnModelCreating(IModelBuilder modelBuilder)
    { }
}

Save

var newModelItem = new ModelItem { ... };
appContext.Items.Add(newModelItem);
await appContext.BatchWriter.SaveChangesAsync(cancellationToken);

Query

var user = await appContext.Query<User>()
    .PartitionKey(id.ToString())
    .ToEntityAsync(cancellationToken);

How to map

Simple Primary Key

Model

public class User
{
    // IMPORTANT
    // The model must have a property named Id
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public SubscriptionLevel SubscriptionLevel { get; set; }

    ...
}

Context

public class AppContext : DynamoSharpContext
{
    public IDynamoDbSet<User> Users { get; private set; } = null!;

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

    /*
    Partition Key and Sort Key are not defined,
    so they will be automatically generated by the library.

    Example Partition Key:
    USER#1

    Example Sort Key:
    USER#1
    */
}

Result

PartitionKeySortKeyIdNameEmailSubscriptionLevel
USER#1USER#11Chrischris@example.comUltimate

Custom Primary Key

Model

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public SubscriptionLevel SubscriptionLevel { get; set; }

    ...
}

Context

public class AppContext : DynamoSharpContext
{
    public IDynamoDbSet<User> Users { get; private set; } = null!;

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

    public override void OnModelCreating(IModelBuilder modelBuilder)
    {
        // Example Partition Key: USR#chris@example.com
        modelBuilder.Entity<User>()
            .HasPartitionKey(u => u.Email, "USR");

        // Example Sort Key: NAME#Chris
        modelBuilder.Entity<User>()
            .HasSortKey(u => u.Name, "NAME");
    }
}

Result

PartitionKeySortKeyIdNameEmailSubscriptionLevel
USR#chris@example.comNAME#Chris1Chrischris@example.comUltimate

Global Secondary Index Primary Key

Model

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }

    ...
}

Context

public class AppContext : DynamoSharpContext
{
    public IDynamoDbSet<User> Users { get; private set; } = null!;

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

    public override void OnModelCreating(IModelBuilder modelBuilder)
    {
        /*
        Partition Key and Sort Key are not defined,
        so they will be automatically generated by the library.

        - User
            Example Partition Key:
            USER#1

            Example Sort Key:
            USER#1

        */

        // Example Global Secondary Index Partition Key: 
        // USER#D1ADDCA5-2B7B-4DE5-A221-C626C0A677F9
        modelBuilder.Entity<User>()
            .HasGlobalSecondaryIndexPartitionKey("GSI1PK", u => u.Id, "USER");

        // Example Global Secondary Index Sort Key:
        // EMAIL#chris@example.com
        modelBuilder.Entity<User>()
            .HasGlobalSecondaryIndexSortKey("GSI1SK", u => u.Email, "EMAIL");
    }
}

Result

PartitionKeySortKeyIdNameEmailGSI1PKGSI1SK
USER#1USER#11Chrischris@example.comUSER#1EMAIL#chris@example.com

Sparse Index

Sparse indexes to provide a global filter on an item type.

Model

public class Organization
{
    public string Name { get; private set; }
    public SubscriptionLevel SubscriptionLevel { get; private set; }
    public List<User> Users { get; private set; }

    ...
}

public class User
{
    public string Name { get; private set; }
    public SubscriptionLevel SubscriptionLevel { get; private set; }

    ...
}

public enum SubscriptionLevel
{
    Admin,
    Member,
    Pro,
    Enterprise
}

Context

public class AppContext : DynamoSharpContext
{
    public IDynamoDbSet<Organization> Organizations { get; private set; } = null!;
    public IDynamoDbSet<User> Users { get; private set; } = null!;

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

    public override void OnModelCreating(IModelBuilder modelBuilder)
    {
        // Example Partition Key: ORG#DynamoSharp
        modelBuilder.Entity<Organization>()
            .HasPartitionKey(o => o.Name, "ORG");

        // Example Sort Key: ORG#DynamoSharp
        modelBuilder.Entity<Organization>()
            .HasSortKey(o => o.Name, "ORG");

        // Example Global Secondary Index Partition Key: ORGANIZATIONS
        modelBuilder.Entity<Organization>()
            .HasGlobalSecondaryIndexPartitionKey("GSI1PK", "ORGANIZATIONS"); // <- SPARSE INDEX

        // Example Global Secondary Index Sort Key: ORG#DynamoSharp
        modelBuilder.Entity<Organization>()
            .HasGlobalSecondaryIndexSortKey("GSI1SK", o => o.Name);

        modelBuilder.Entity<Organization>()
            .HasOneToMany(o => o.Users);


        // Example Sort Key: USER#Chris
        modelBuilder.Entity<User>()
            .HasSortKey(u => u.Name, "USER");
    }
}

Result

PartitionKeySortKeyNameSubscriptionLevelGSI1PKGSI1SK
ORG#AmazonORG#AmazonAmazonAdminORGANIZATIONSAmazon
ORG#DynamoSharpORG#DynamoSharpDynamoSharpAdminORGANIZATIONSDynamoSharp
ORG#AmazonUSER#ChrisChrisMember

Primary Key with nested properties

Model

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string ZipCode { get; set; }
    public string Country { get; set; }

    ....
}

public class Store
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
    public Address Address { get; set; }

    ...
}

Context

public class AppContext : DynamoSharpContext
{
    public IDynamoDbSet<User> Users { get; private set; } = null!;

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

    public override void OnModelCreating(
        IModelBuilder modelBuilder)
    {
        // Example Partition Key: COUNTRY#Mexico
        modelBuilder.Entity<Store>()
            .HasPartitionKey(s => s.Country, "COUNTRY");

        // Example Sort Key: STATE#Tabasco
        modelBuilder.Entity<Store>()
            .HasSortKey(s => s.State, "STATE");

        // Example Global Secondary Index Partition Key: 
        // CITY#Villahermosa
        modelBuilder.Entity<Store>()
            .HasGlobalSecondaryIndexPartitionKey("GSI1PK", s => s.City, "CITY");

        // Example Global Secondary Index Sort Key:
        // ZIPCODE#00000
        modelBuilder.Entity<Store>()
            .HasGlobalSecondaryIndexSortKey("GSI1SK", s => u.ZipCode, "ZIPCODE");
    }
}

Result

PartitionKeySortKeyIdNamePhoneEmailAddressGSI1PKGSI1SK
COUNTRY#MexicoSTATE#Tabasco1Tacos El Guero1234567890example@example.com{ ... }CITY#VillahermosaxZIPCODE#00000x

Primary Key with hierarchical data

Model

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string ZipCode { get; set; }
    public string Country { get; set; }

    ....
}

public class Store
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
    public Address Address { get; set; }

    ...
}

Context

public class AppContext : DynamoSharpContext
{
    public IDynamoDbSet<User> Users { get; private set; } = null!;

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

    public override void OnModelCreating(
        IModelBuilder modelBuilder)
    {
        // Example Partition Key: COUNTRY#Mexico
        modelBuilder.Entity<Store>()
            .HasPartitionKey(s => s.Country, "COUNTRY");

        // Example Sort Key: STATE#Tabasco#CITY#Villahermosa#ZIPCODE#00000
        modelBuilder.Entity<Store>()
            .HasSortKey(s => s.State, "STATE")
            .Include(s => s.Address.City, "CITY");

        // Example Global Secondary Index Partition Key: Mexico
        modelBuilder.Entity<Store>()
            .HasGlobalSecondaryIndexPartitionKey("GSI1PK", s => s.Country);

        // Example Global Secondary Index Sort Key:
        // Tabasco#Villahermosa#00000
        modelBuilder.Entity<Store>()
            .HasGlobalSecondaryIndexSortKey("GSI1SK", s => s.State)
            .Include(s => s.Address.City, "CITY")
            .Include(s => s.Address.ZipCode, "ZIPCODE");
    }
}

Result

PartitionKeySortKeyIdNamePhoneEmailAddressGSI1PKGSI1SK
COUNTRY#MexicoSTATE#Tabasco#CITY#Villahermosa1Tacos El Guero1234567890example@example.com{ ... }MexicoTabasco#Villahermosa#00000

One to many

Model

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;

    ...
}

Context

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

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

    public override void OnModelCreating(IModelBuilder modelBuilder)
    {
        /*
        Partition Key and Sort Key are not defined,
        so they will be automatically generated by the library.

        - Order
            Example Partition Key:
            ORDER#1

            Example Sort Key:
            ORDER#1
        
        - Item
            Example Partition Key:
            ORDER#1
            
            Example Sort Key:
            ITEM#2
        */

        modelBuilder.Entity<Order>()
            .HasOneToMany(o => o.Items);
    }
}

Result

PartitionKeySortKeyIdBuyerIdAddressStatusDateProductNameUnitPriceUnits
ORDER#1ORDER#1126{ ... }Shipped2024-08-28T19:41:50.9509387-06:00
ORDER#1ITEM#22Product 126.05

One to many with custom primary key

Model

public class Organization
{
    public string Name { get; private set; }
    public SubscriptionLevel SubscriptionLevel { get; private set; }
    public List<User> Users  { get; private set; }

    ...
}

public class User
{
    public string Name { get; private set; }
    public SubscriptionLevel SubscriptionLevel { get; private set; }

    ...
}

public enum SubscriptionLevel
{
    Admin,
    Member,
    Pro,
    Enterprise
}

Context

public class AppContext : DynamoSharpContext
{
    public IDynamoDbSet<Organization> Organizations { get; private set; } = null!;
    public IDynamoDbSet<User> Users { get; private set; } = null!;

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

    public override void OnModelCreating(
        IModelBuilder modelBuilder)
    {
        // Example Partition Key: ORG#DynamoSharp
        modelBuilder.Entity<Organization>()
            .HasPartitionKey(o => o.Name, "ORG");

        // Example Sort Key: ORG#DynamoSharp
        modelBuilder.Entity<Organization>()
            .HasSortKey(o => o.Name, "ORG");

        modelBuilder.Entity<Organization>()
            .HasOneToMany(o => o.Users);

        // Example Sort Key: USER#Chris
        modelBuilder.Entity<User>()
            .HasSortKey(o => o.Name, "USER");
    }
}

Result

PartitionKeySortKeyNameSubscriptionLevel
ORG#AmazonORG#AmazonAmazonAdmin
ORG#AmazonUSER#ChrisChrisMember

Many to many

Model

public class Movie
{
    // IMPORTANT
    // The model must have a property named Id
    public int Id { get; set; }
    public string Title { get; set; }
    public List<Performance> Actors { get; set; }

    ...
}

public class Performance
{
    // IMPORTANT
    // The model must have both Ids, in this case MovieId and ActorId
    public int MovieId { get; set; }
    public int ActorId { get; set; }
    public string MovieTitle { get; set; }
    public string ActorName { get; set; }
    public string RoleName { get; set; }

    ...
}

public partial class Actor
{
    // IMPORTANT
    // The model must have a property named Id
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Performance> Movies { get; set; }

    ...
}

Context

public class AppContext : DynamoSharpContext
{
    public IDynamoDbSet<Movie> Movies { get; private set; } = null!;
    public IDynamoDbSet<Actor> Actors { get; private set; } = null!;

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

    public override void OnModelCreating(IModelBuilder modelBuilder)
    {
        /*
        Partition Key and Sort Key are not defined,
        so they will be automatically generated by the library.

        - Movie
            Example Partition Key:
            MOVIE#1

            Example Sort Key:
            MOVIE#1
        
        - Actor
            Example Partition Key:
            ACTOR#2

            Example Sort Key:
            ACTOR#2

            Global Secondary Index Partition Key:
            ACTOR#2

            Global Secondary Index Sort Key:
            ACTOR#2

        - Performance
            Example Partition Key:
            MOVIE#1

            Example Sort Key:
            ACTOR#2

            Global Secondary Index Partition Key:
            ACTOR#2

            Global Secondary Index Sort Key:
            MOVIE#1
        */

        modelBuilder.Entity<Movie>()
            .HasManyToMany(m => m.Actors);

        modelBuilder.Entity<Actor>()
            .HasManyToMany(a => a.Movies);
    }
}

Result

PartitionKeySortKeyIdTitleMovieIdActorIdMovieTitleActorNameRoleNameNameGSI1PKGSI1SK
MOVIE#1MOVIE#11The Matrix
MOVIE#1ACTOR#212The MatrixKeanu ReevesNeoACTOR#2MOVIE#1
ACTOR#2ACTOR#22Keanu ReevesACTOR#2ACTOR#2

Many to many with custom primary key

Model

public class Movie
{
    public string Title { get; set; }
    public List<Performance> Actors { get; set; }

    ...
}

public class Performance
{
    public string MovieTitle { get; set; }
    public string ActorName { get; set; }
    public string RoleName { get; set; }

    ...
}

public partial class Actor
{
    public string Name { get; set; }
    public List<Performance> Movies { get; set; }

    ...
}

Context

public class AppContext : DynamoSharpContext
{
    public IDynamoDbSet<Movie> Movies { get; private set; } = null!;
    public IDynamoDbSet<Actor> Actors { get; private set; } = null!;

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

    public override void OnModelCreating(
        IModelBuilder modelBuilder)
    {
        MovieModelBuilder(modelBuilder);

        ActorModelBuilder(modelBuilder);

        PerformanceModelBuilder(modelBuilder);
    }

    private void MovieModelBuilder(
        IModelBuilder modelBuilder)
    {
        // Example Partition Key: MOVIE#The Matrix
        modelBuilder.Entity<Movie>()
            .HasPartitionKey(m => m.Title, "MOVIE");

        // Example Sort Key: MOVIE#The Matrix
        modelBuilder.Entity<Movie>()
            .HasSortKey(m => m.Title, "MOVIE");

        modelBuilder.Entity<Movie>()
            .HasManyToMany(m => m.Actors);
    }

    private void ActorModelBuilder(
        IModelBuilder modelBuilder)
    {
        // Example Partition Key: ACTOR#Keanu Reeves
        modelBuilder.Entity<Actor>()
            .HasPartitionKey(a => a.Name, "ACTOR");

        // Example Sort Key: ACTOR#Keanu Reeves
        modelBuilder.Entity<Actor>()
            .HasSortKey(a => a.Name, "ACTOR");

        modelBuilder.Entity<Actor>()
            .HasManyToMany(a => a.Movies);

        // Example Global Secondary Index Partition Key: ACTOR#Keanu Reeves
        modelBuilder.Entity<Actor>()
            .HasGlobalSecondaryIndexPartitionKey("GSI1PK", a => a.Name, "ACTOR");

        // Example Global Secondary Index Sort Key: ACTOR#Keanu Reeves
        modelBuilder.Entity<Actor>()
            .HasGlobalSecondaryIndexSortKey("GSI1SK", a => a.Name, "ACTOR");
    }

    private void PerformanceModelBuilder(
        IModelBuilder modelBuilder)
    {
        // Example Partition Key: MOVIE#The Matrix
        modelBuilder.Entity<Performance>()
            .HasPartitionKey(p => p.MovieTitle, "MOVIE");

        // Example Sort Key: ACTOR#Keanu Reeves
        modelBuilder.Entity<Performance>()
            .HasSortKey(p => p.ActorName, "ACTOR");

        // Example Global Secondary Index Partition Key: ACTOR#Keanu Reeves
        modelBuilder.Entity<Performance>()
            .HasGlobalSecondaryIndexPartitionKey("GSI1PK", p => p.ActorName, "ACTOR");

        // Example Global Secondary Index Sort Key: MOVIE#The Matrix
        modelBuilder.Entity<Performance>()
            .HasGlobalSecondaryIndexSortKey("GSI1SK", p => p.MovieTitle, "MOVIE");
    }
}

Result

PartitionKeySortKeyTitleMovieTitleActorNameRoleNameNameGSI1PKGSI1SK
MOVIE#The MatrixMOVIE#The MatrixThe Matrix
MOVIE#The MatrixACTOR#Keanu ReevesThe MatrixKeanu ReevesNeoACTOR#Keanu ReevesMOVIE#The Matrix
ACTOR#Keanu ReevesACTOR#Keanu ReevesKeanu ReevesACTOR#Keanu ReevesACTOR#Keanu Reeves

How to save

Save

appContext.Users.Add(newUser);
        
// Save with batch 
await appContext.BatchWriter.SaveChangesAsync(cancellationToken);
// Save with transaction
await appContext.TransactWriter.SaveChangesAsync(cancellationToken);

Update

appContext.Users.Add(newUser);
await appContext.BatchWriter.SaveChangesAsync(cancellationToken);

newUser.Email = "example@example.com";

// Update with batch
await appContext.BatchWriter.SaveChangesAsync(cancellationToken);
// Update with transaction
await appContext.TransactWriter.SaveChangesAsync(cancellationToken);

Remove

// Find user by id...

_appContext.Users.Remove(newUser);
        
// Save with batch 
await appContext.BatchWriter.SaveChangesAsync(cancellationToken);
// Save with transaction
await appContext.TransactWriter.SaveChangesAsync(cancellationToken);

How to query

Query by partition key

var user = await appContext.Query<User>()
    .PartitionKey($"USER#{id}")
    .ToEntityAsync(cancellationToken);

Query by partition key and sort key

 var item = await appContext.Query<Item>()
    .PartitionKey($"ORDER#{orderId}")
    .SortKey(QueryOperator.Equal, $"ITEM#{itemId}")
    .ToEntityAsync(cancellationToken);

Query by partition key using global secondary index

var user = await appContext.Query<User>()
    .IndexName("GSI1PK-GSI1SK-index")
    .PartitionKey("GSI1PK", $"USER#{id}")
    .ToEntityAsync(cancellationToken);

Query by partition key and sort key using global secondary index

var item = await appContext.Query<Item>()
    .IndexName("GSI1PK-GSI1SK-index")
    .PartitionKey("GSI1PK", $"ORDER#{orderId}")
    .SortKey("GSI1SK", QueryOperator.BeginsWith, "ITEM#")
    .ToListAsync(cancellationToken);

Query with filter

var item = await appContext.Query<Item>()
    .IndexName("GSI1PK-GSI1SK-index")
    .PartitionKey("GSI1PK", $"ORDER#{orderId}")
    .SortKey("GSI1SK", QueryOperator.BeginsWith, "ITEM#")
    .Filter(item => item.Units >= 5 && item.Units < 10)
    .ToListAsync(cancellationToken);

Query with Limit, ConsistentRead, ScanIndexForward and AsNoTracking

var items = await appContext.Query<Item>()
    .IndexName("GSI1PK-GSI1SK-index")
    .PartitionKey("GSI1PK", $"ORDER#{orderId}")
    .SortKey("GSI1SK", QueryOperator.BeginsWith, "ITEM#")
    .Limit(limit)
    .ConsistentRead()
    .ScanIndexForward()
    .AsNoTracking()
    .ToListAsync(cancellationToken);

Version control (Optimistic locking)

Add config

builder.Services.AddDynamoSharpContext<AppContext>(
    new TableSchema.Builder()
        .WithTableName("dynamosharp")
        .WithVersionName("v")// attribute used for optimistic locking
        .Build()
);

Context

public class EcommerceContext : DynamoSharpContext
{
    ...

    public override void OnModelCreating(IModelBuilder modelBuilder)
    {
        ...

        // Enable optimistic locking for Order entity
        modelBuilder.Entity<Order>()
            .HasVersioning(); 

        // Enable optimistic locking for Item entity
        modelBuilder.Entity<Item>()
            .HasVersioning();
    }
}

⚠️Thread Safety Considerations⚠️

DynamoSharp does not support concurrent operations on the same DynamoSharpContext instance. Avoid running multiple asynchronous queries in parallel or accessing the context from multiple threads simultaneously. Always await async calls immediately or use separate DynamoSharpContext instances for parallel operations.