Skip to content

ChrixApp/DynamoSharp

Repository files navigation

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

PartitionKey SortKey Id Name Email SubscriptionLevel
USER#1 USER#1 1 Chris [email protected] Ultimate

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#[email protected]
        modelBuilder.Entity<User>()
            .HasPartitionKey(u => u.Email, "USR");

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

Result

PartitionKey SortKey Id Name Email SubscriptionLevel
USR#[email protected] NAME#Chris 1 Chris [email protected] Ultimate

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#[email protected]
        modelBuilder.Entity<User>()
            .HasGlobalSecondaryIndexSortKey("GSI1SK", u => u.Email, "EMAIL");
    }
}

Result

PartitionKey SortKey Id Name Email GSI1PK GSI1SK
USER#1 USER#1 1 Chris [email protected] USER#1 EMAIL#[email protected]

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

PartitionKey SortKey Name SubscriptionLevel GSI1PK GSI1SK
ORG#Amazon ORG#Amazon Amazon Admin ORGANIZATIONS Amazon
ORG#DynamoSharp ORG#DynamoSharp DynamoSharp Admin ORGANIZATIONS DynamoSharp
ORG#Amazon USER#Chris Chris Member

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

PartitionKey SortKey Id Name Phone Email Address GSI1PK GSI1SK
COUNTRY#Mexico STATE#Tabasco 1 Tacos El Guero 1234567890 [email protected] { ... } CITY#Villahermosax ZIPCODE#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

PartitionKey SortKey Id Name Phone Email Address GSI1PK GSI1SK
COUNTRY#Mexico STATE#Tabasco#CITY#Villahermosa 1 Tacos El Guero 1234567890 [email protected] { ... } Mexico Tabasco#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

PartitionKey SortKey Id BuyerId Address Status Date ProductName UnitPrice Units
ORDER#1 ORDER#1 1 26 { ... } Shipped 2024-08-28T19:41:50.9509387-06:00
ORDER#1 ITEM#2 2 Product 1 26.0 5

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

PartitionKey SortKey Name SubscriptionLevel
ORG#Amazon ORG#Amazon Amazon Admin
ORG#Amazon USER#Chris Chris Member

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

PartitionKey SortKey Id Title MovieId ActorId MovieTitle ActorName RoleName Name GSI1PK GSI1SK
MOVIE#1 MOVIE#1 1 The Matrix
MOVIE#1 ACTOR#2 1 2 The Matrix Keanu Reeves Neo ACTOR#2 MOVIE#1
ACTOR#2 ACTOR#2 2 Keanu Reeves ACTOR#2 ACTOR#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

PartitionKey SortKey Title MovieTitle ActorName RoleName Name GSI1PK GSI1SK
MOVIE#The Matrix MOVIE#The Matrix The Matrix
MOVIE#The Matrix ACTOR#Keanu Reeves The Matrix Keanu Reeves Neo ACTOR#Keanu Reeves MOVIE#The Matrix
ACTOR#Keanu Reeves ACTOR#Keanu Reeves Keanu Reeves ACTOR#Keanu Reeves ACTOR#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 = "[email protected]";

// 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.

About

an ORM with a focus on single-table design

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages