Amazon DynamoDB · Update
How an update finds its item
How an update finds its item in DynamoDB, in four scenarios: an order by its id and its customer, by its key's attributes, every order a query of the customer's partition finds, and a match no partition answers.
Set up for this page
The scenarios build on the sample application: SampleContext.cs (the dbContext every sample is given); SampleOutput.cs (Step and Show, which print); SampleContext.Application.cs (dbContext.Service, the application's ARX service); SampleModel.cs (the DynamoDB layout and the schema catalog); SampleContext.Direct.cs (dbContext.Direct); DynamoDirectHelper.cs (the checks behind dbContext.Direct).
dbContext.Direct is the samples' own checking code on the plain driver, not part of ARX: leave it out of your application.
Before scenario 1 the sample runs this, so the service and the rows the scenarios use are there:
var customer = new ArxEntity("customer");
customer["customer_id"] = "C-D211";
customer["name"] = "Tailspin Toys";
await service.CreateAsync(customer);
var orders = new[] { "SO-D211", "SO-D212" }.Select(number =>
{
var order = new ArxEntity("sales_order");
order["customer_id"] = "C-D211";
order["order_number"] = number;
order["status"] = "OPEN";
order["ordered_on"] = "2026-11-02";
return order;
}).ToList();
await service.CreateMultipleAsync(orders);1 By the id and the customer
The id Create returned plus the customer: ship SO-D211 (with its date, so ARX rewrites the GSI1 keys).
Already in scope: service (ArxDynamoDbService)
var change = new ArxEntity("sales_order") { RecordId = ArxRecordId.Of("order_number", "SO-D211") };
change["customer_id"] = "C-D211";
change["status"] = "SHIPPED";
change["ordered_on"] = "2026-11-02";
await service.UpdateAsync(change);
Console.WriteLine("SO-D211 shipped");Check it in the database
A PartiQL statement: run it in NoSQL Workbench, or with aws dynamodb execute-statement --statement, after the code has run.
SELECT "status", "GSI1PK" FROM "crm" WHERE "PK" = 'CUSTOMER#C-D211' AND "SK" = 'ORDER#SO-D211'Expect: 1 item, status "SHIPPED", GSI1PK "STATUS#SHIPPED".
2 By the key's attributes
ArxMatch.Columns naming exactly the key's attributes: the same thing, said explicitly.
Already in scope: service (ArxDynamoDbService)
var change = new ArxEntity("sales_order");
change["customer_id"] = "C-D211";
change["order_number"] = "SO-D212";
change["status"] = "CANCELLED";
change["ordered_on"] = "2026-11-02";
await service.UpdateAsync(change, new ArxUpdateOptions { Match = ArxMatch.Columns("customer_id", "order_number") });
Console.WriteLine("SO-D212 cancelled");Check it in the database
A PartiQL statement: run it in NoSQL Workbench, or with aws dynamodb execute-statement --statement, after the code has run.
SELECT "status" FROM "crm" WHERE "PK" = 'CUSTOMER#C-D211' AND "SK" = 'ORDER#SO-D212'Expect: 1 item, status "CANCELLED".
3 Through a query
Every order of the customer, through a query of its partition: each found order updated by its key.
Already in scope: service (ArxDynamoDbService)
var change = new ArxEntity("sales_order");
change["customer_id"] = "C-D211";
change["priority"] = "high";
int changed = await service.UpdateAsync(change, new ArxUpdateOptions { Match = ArxMatch.Columns("customer_id") });
Console.WriteLine($"{changed} orders marked high priority");Check it in the database
A PartiQL statement: run it in NoSQL Workbench, or with aws dynamodb execute-statement --statement, after the code has run.
SELECT "order_number", "priority" FROM "crm" WHERE "PK" = 'CUSTOMER#C-D211' AND begins_with("SK", 'ORDER#')Expect: 2 items, SO-D211 and SO-D212, both priority "high".
4 No partition
Every order of a date: no partition key in the match, so ARX8001 rather than a scan.
Already in scope: service (ArxDynamoDbService)
var change = new ArxEntity("sales_order");
change["ordered_on"] = "2026-11-02";
change["priority"] = "low";
try
{
await service.UpdateAsync(change, new ArxUpdateOptions { Match = ArxMatch.Columns("ordered_on") });
}
catch (ArxNotSupportedException exception)
{
Console.WriteLine(exception.Message);
}Check it in the database
A PartiQL statement: run it in NoSQL Workbench, or with aws dynamodb execute-statement --statement, after the code has run.
SELECT "order_number", "priority" FROM "crm" WHERE "PK" = 'CUSTOMER#C-D211' AND begins_with("SK", 'ORDER#')Expect: 2 items, both still priority "high".
Good to know
DynamoDB writes an item by its full key. An order's key is its customer and its number, so its id alone isn't enough: give the customer too. A match by other columns works when they give a partition key, of the table or an index: ARX queries it, then updates each item found by its key, all or nothing (at most 100), each conditioned on still matching. Without a partition it is ARX8001: a write never scans. When an attribute an index is keyed by changes (the status), give the index key's other attributes too (the date): ARX rewrites the index keys from them.
Run the complete sample
The code above comes from one program, built in a fresh project and run against the database. To run it yourself, here is every file and step.
The program, its files and how to run it
Run it locally
- .NET SDK: .NET SDK 10.0.100 or later 10.0.x (target
net10.0). - Create the project:
Terminal dotnet new console -n ArxSample -f net10.0 cd ArxSample - Install the packages:
Terminal dotnet add package ARX.SDK.DynamoDb --version 1.0.0-rc.1 - Database: a Amazon DynamoDB, or DynamoDB Local for development (tested with DynamoDB Local 2.5.2) database with the sample schema.
- Configuration: set
ARX_SAMPLES_DYNAMODBto its connection string. Nothing is hard-coded. - Source code: add the files of the sample application this sample uses (
SampleContext.cs,SampleOutput.cs,SampleContext.Application.cs,SampleModel.cs,SampleContext.Direct.cs,DynamoDirectHelper.cs), then replaceProgram.csand add the other file below, with the file names shown. - Run it:
Terminal dotnet run - Expected output: below.
The files
Runs the sample. Generated for this page; it reads the connection string from the environment and first remakes the sample database from sample-schema.json.
using ArxSamples.DynamoDb;
using ArxSamples.DynamoDb.Operations.Update.Basics;
// The connection string comes from the environment, never from the code (see "Configure the connection").
var connectionString = Environment.GetEnvironmentVariable("ARX_SAMPLES_DYNAMODB")
?? throw new InvalidOperationException("Set the ARX_SAMPLES_DYNAMODB environment variable to your Amazon DynamoDB connection string.");
var dbContext = new SampleContext(connectionString);
// Every run starts from the sample data: this deletes and makes again what sample-schema.json lists
// (save the file in the project folder, see Get started).
await dbContext.Direct.ResetAsync();
await new Basic2_HowToFindTheItems().RunAsync(dbContext);
The sample, as it is in the repository.
using ARX.SDK;
using ARX.SDK.DynamoDb.Services;
using ARX.SDK.Services;
using static ArxSamples.DynamoDb.SampleOutput;
namespace ArxSamples.DynamoDb.Operations.Update.Basics;
/// <summary>
/// How an update finds its item in DynamoDB, in four scenarios: an order by its id and its customer, by its key's
/// attributes, every order a query of the customer's partition finds, and a match no partition answers.
/// </summary>
/// <remarks>
/// DynamoDB writes an item by its full key. An order's key is its customer and its number, so its id alone isn't
/// enough: give the customer too. A match by other columns works when they give a partition key, of the table or an index:
/// ARX queries it, then updates each item found by its key, all or nothing (at most 100), each conditioned on still
/// matching. Without a partition it is ARX8001: a write never scans. When an attribute an index is keyed by changes (the status), give the index key's other attributes too (the date): ARX rewrites the index keys from them.
/// </remarks>
public sealed class Basic2_HowToFindTheItems : ISample
{
public string Name => "update-basics-2-how-to-find-the-items";
public string Title => "How an update finds its item";
public async Task RunAsync(SampleContext dbContext)
{
var customer = new ArxEntity("customer");
customer["customer_id"] = "C-D211";
customer["name"] = "Tailspin Toys";
await dbContext.Service.CreateAsync(customer);
var orders = new[] { "SO-D211", "SO-D212" }.Select(number =>
{
var order = new ArxEntity("sales_order");
order["customer_id"] = "C-D211";
order["order_number"] = number;
order["status"] = "OPEN";
order["ordered_on"] = "2026-11-02";
return order;
}).ToList();
await dbContext.Service.CreateMultipleAsync(orders);
await Scenario1_ByIdAndCustomerAsync(dbContext.Service);
await Scenario2_ByTheKeysAttributesAsync(dbContext.Service);
await Scenario3_ThroughAQueryAsync(dbContext.Service);
await Scenario4_NoPartitionAsync(dbContext.Service);
}
/// <summary>1. The id Create returned plus the customer: ship SO-D211 (with its date, so ARX rewrites the GSI1 keys).</summary>
private static async Task Scenario1_ByIdAndCustomerAsync(ArxDynamoDbService service)
{
Step("1. By the id and the customer");
var change = new ArxEntity("sales_order") { RecordId = ArxRecordId.Of("order_number", "SO-D211") };
change["customer_id"] = "C-D211";
change["status"] = "SHIPPED";
change["ordered_on"] = "2026-11-02";
await service.UpdateAsync(change);
Show("SO-D211 shipped");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT "status", "GSI1PK" FROM "crm" WHERE "PK" = 'CUSTOMER#C-D211' AND "SK" = 'ORDER#SO-D211'
* -- Expect: 1 item, status "SHIPPED", GSI1PK "STATUS#SHIPPED".
* ===========================================================
*/
}
/// <summary>2. ArxMatch.Columns naming exactly the key's attributes: the same thing, said explicitly.</summary>
private static async Task Scenario2_ByTheKeysAttributesAsync(ArxDynamoDbService service)
{
Step("2. By the key's attributes");
var change = new ArxEntity("sales_order");
change["customer_id"] = "C-D211";
change["order_number"] = "SO-D212";
change["status"] = "CANCELLED";
change["ordered_on"] = "2026-11-02";
await service.UpdateAsync(change, new ArxUpdateOptions { Match = ArxMatch.Columns("customer_id", "order_number") });
Show("SO-D212 cancelled");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT "status" FROM "crm" WHERE "PK" = 'CUSTOMER#C-D211' AND "SK" = 'ORDER#SO-D212'
* -- Expect: 1 item, status "CANCELLED".
* ===========================================================
*/
}
/// <summary>3. Every order of the customer, through a query of its partition: each found order updated by its key.</summary>
private static async Task Scenario3_ThroughAQueryAsync(ArxDynamoDbService service)
{
Step("3. Through a query");
var change = new ArxEntity("sales_order");
change["customer_id"] = "C-D211";
change["priority"] = "high";
int changed = await service.UpdateAsync(change, new ArxUpdateOptions { Match = ArxMatch.Columns("customer_id") });
Show($"{changed} orders marked high priority");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT "order_number", "priority" FROM "crm" WHERE "PK" = 'CUSTOMER#C-D211' AND begins_with("SK", 'ORDER#')
* -- Expect: 2 items, SO-D211 and SO-D212, both priority "high".
* ===========================================================
*/
}
/// <summary>4. Every order of a date: no partition key in the match, so ARX8001 rather than a scan.</summary>
private static async Task Scenario4_NoPartitionAsync(ArxDynamoDbService service)
{
Step("4. No partition");
var change = new ArxEntity("sales_order");
change["ordered_on"] = "2026-11-02";
change["priority"] = "low";
try
{
await service.UpdateAsync(change, new ArxUpdateOptions { Match = ArxMatch.Columns("ordered_on") });
}
catch (ArxNotSupportedException exception)
{
ShowError(exception);
}
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT "order_number", "priority" FROM "crm" WHERE "PK" = 'CUSTOMER#C-D211' AND begins_with("SK", 'ORDER#')
* -- Expect: 2 items, both still priority "high".
* ===========================================================
*/
}
}
Expected output
What dotnet run printed when the sample was verified, on a freshly created sample schema. Ids differ when the tables already hold rows.
> 1. By the id and the customer
SO-D211 shipped
> 2. By the key's attributes
SO-D212 cancelled
> 3. Through a query
2 orders marked high priority
> 4. No partition
caught ArxNotSupportedException: ARX8001 NotSupported: Update of 'sales_order' by Columns(ordered_on): DynamoDB finds items by a partition key, and these columns give neither the table's (customer_id) nor an index's. Match by the key, or by columns that include an index's partition key attributes.