DynamoSharp 0.1.2
dotnet add package DynamoSharp --version 0.1.2
NuGet\Install-Package DynamoSharp -Version 0.1.2
<PackageReference Include="DynamoSharp" Version="0.1.2" />
<PackageVersion Include="DynamoSharp" Version="0.1.2" />
<PackageReference Include="DynamoSharp" />
paket add DynamoSharp --version 0.1.2
#r "nuget: DynamoSharp, 0.1.2"
#:package DynamoSharp@0.1.2
#addin nuget:?package=DynamoSharp&version=0.1.2
#tool nuget:?package=DynamoSharp&version=0.1.2
DynamoSharp
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
- Install
- Configuration
- Implementation
- How to map
- How to save
- How to query
- Version control
- Thread Safety Considerations
Installation
dotnet add package DynamoSharp
Configuration
builder.Services.AddDynamoSharp(); //Add internal dependencies
builder.Services.AddDynamoSharpContext<AppContext>(
new TableSchema.Builder()
.WithTableName("dynamosharp")
.WithPartitionKeyName("PK")
.WithSortKeyName("SK")
.AddGlobalSecondaryIndex("GSI1PK-GSI1SK-index", "GSI1PK", "GSI1SK")
.Build()
);
Implementation
public class ModelItem { }
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)
{ }
}
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 | SubscriptionLevel | |
---|---|---|---|---|---|
USER#1 | USER#1 | 1 | Chris | chris@example.com | 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#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
PartitionKey | SortKey | Id | Name | SubscriptionLevel | |
---|---|---|---|---|---|
USR#chris@example.com | NAME#Chris | 1 | Chris | chris@example.com | 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#chris@example.com
modelBuilder.Entity<User>()
.HasGlobalSecondaryIndexSortKey("GSI1SK", o => u.Email, "EMAIL");
}
}
Result
PartitionKey | SortKey | Id | Name | GSI1PK | GSI1SK | |
---|---|---|---|---|---|---|
USER#1 | USER#1 | 1 | Chris | chris@example.com | USER#1 | EMAIL#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(u => u.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(u => u.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(u => u.Country, "COUNTRY");
// Example Sort Key: STATE#Tabasco
modelBuilder.Entity<Store>()
.HasSortKey(u => u.State, "STATE");
// Example Global Secondary Index Partition Key:
// CITY#Villahermosa
modelBuilder.Entity<Store>()
.HasGlobalSecondaryIndexPartitionKey("GSI1PK", u => u.City, "CITY");
// Example Global Secondary Index Sort Key:
// ZIPCODE#00000
modelBuilder.Entity<Store>()
.HasGlobalSecondaryIndexSortKey("GSI1SK", o => u.ZipCode, "ZIPCODE");
}
}
Result
PartitionKey | SortKey | Id | Name | Phone | Address | GSI1PK | GSI1SK | |
---|---|---|---|---|---|---|---|---|
COUNTRY#Mexico | STATE#Tabasco | 1 | Tacos El Guero | 1234567890 | example@example.com | { ... } | 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(u => u.Country, "COUNTRY");
// Example Sort Key: STATE#Tabasco#CITY#Villahermosa#ZIPCODE#00000
modelBuilder.Entity<Store>()
.HasSortKey(u => u.State, "STATE")
.Include(s => s.Address.City, "CITY");
// Example Global Secondary Index Partition Key: Mexico
modelBuilder.Entity<Store>()
.HasGlobalSecondaryIndexPartitionKey("GSI1PK", u => u.Country);
// Example Global Secondary Index Sort Key:
// Tabasco#Villahermosa#00000
modelBuilder.Entity<Store>()
.HasGlobalSecondaryIndexSortKey("GSI1SK", o => u.State)
.Include(s => s.Address.City, "CITY")
.Include(s => s.Address.ZipCode, "ZIPCODE");
}
}
Result
PartitionKey | SortKey | Id | Name | Phone | Address | GSI1PK | GSI1SK | |
---|---|---|---|---|---|---|---|---|
COUNTRY#Mexico | STATE#Tabasco#CITY#Villahermosa | 1 | Tacos El Guero | 1234567890 | example@example.com | { ... } | 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(u => u.Name, "ORG");
modelBuilder.Entity<Organization>()
.HasOneToMany(u => u.Users);
// Example Sort Key: USER#Chris
modelBuilder.Entity<User>()
.HasSortKey(u => u.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 = "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
// 'v' represents the version attribute for optimistic locking
builder.Services.AddDynamoSharpContext<EcommerceContext>("dynamosharp", "v");
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.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- AWSSDK.DynamoDBv2 (>= 4.0.2.3)
- AWSSDK.Extensions.NETCore.Setup (>= 4.0.2)
- EfficientDynamoDb (>= 0.9.18-alpha.0.6)
- EfficientDynamoDb.Credentials.AWSSDK (>= 0.9.3)
- FluentAssertions (>= 8.5.0)
- Microsoft.AspNetCore.TestHost (>= 8.0.18)
- Microsoft.Extensions.DependencyInjection (>= 8.0.1)
- Moq (>= 4.20.72)
- Newtonsoft.Json (>= 13.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.