Amazon DynamoDB · RetrieveMultiple
Retrieve many records
Read many items with ArxQueryExpression, in three scenarios: a customer's orders, newest first and filtered, a range of the sort key, and a query DynamoDB can't answer without a scan.
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 orders = Enumerable.Range(1, 6).Select(i =>
{
var order = new ArxEntity("sales_order");
order["customer_id"] = "C-D411";
order["order_number"] = $"SO-D41{i}";
order["status"] = i % 3 == 0 ? "CANCELLED" : "OPEN";
order["ordered_on"] = $"2026-11-0{i}";
order["total"] = 10m * i;
return order;
}).ToList();
await service.CreateMultipleAsync(orders);1 A customer's orders
The customer's open orders, newest number first.
Already in scope: service (ArxDynamoDbService)
var query = new ArxQueryExpression("sales_order");
query.ColumnSet.AddColumns("order_number", "total");
query.Criteria.AddCondition("customer_id", ArxConditionOperator.Equal, "C-D411");
query.Criteria.AddCondition("status", ArxConditionOperator.Equal, "OPEN");
query.AddOrder("order_number", ArxOrderType.Descending);
var found = await service.RetrieveMultipleAsync(query);
Console.WriteLine(string.Join(", ", found.Entities.Select(x => $"{x["order_number"]} {x["total"]}")));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", "total" FROM "crm" WHERE "PK" = 'CUSTOMER#C-D411' AND begins_with("SK", 'ORDER#') AND "status" = 'OPEN'Expect: 4 items, SO-D411, SO-D412, SO-D414, SO-D415.
2 A range of the sort key
Order numbers from SO-D413 on: a range of the sort key, read in the key's order.
Already in scope: service (ArxDynamoDbService)
var query = new ArxQueryExpression("sales_order");
query.ColumnSet.AddColumns("order_number");
query.Criteria.AddCondition("customer_id", ArxConditionOperator.Equal, "C-D411");
query.Criteria.AddCondition("order_number", ArxConditionOperator.GreaterEqual, "SO-D413");
var found = await service.RetrieveMultipleAsync(query);
Console.WriteLine(string.Join(", ", found.Entities.Select(x => x["order_number"])));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" FROM "crm" WHERE "PK" = 'CUSTOMER#C-D411' AND "SK" >= 'ORDER#SO-D413'Expect: 4 items, SO-D413 to SO-D416.
3 No partition
Every order over 30, whoever the customer: no partition, so ARX8001 rather than a hidden scan.
Already in scope: service (ArxDynamoDbService)
var query = new ArxQueryExpression("sales_order");
query.Criteria.AddCondition("total", ArxConditionOperator.GreaterThan, 30m);
try
{
await service.RetrieveMultipleAsync(query);
}
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" FROM "crm" WHERE "entity_type" = 'sales_order' AND "total" > 30Expect: the orders over 30 (PartiQL scans the table for this).
Good to know
A query needs the partition: an Equal condition on the attributes of the partition key (here the customer). Conditions on the sort key's attribute narrow the range; the others filter what was read. Orders go by the sort key only. Without a partition, ARX refuses (ARX8001) unless you allow a scan (Advanced 1).
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.RetrieveMultiple.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 Basic1_RetrieveManyRecords().RunAsync(dbContext);
The sample, as it is in the repository.
using ARX.SDK;
using ARX.SDK.DynamoDb.Services;
using ARX.SDK.Query;
using ARX.SDK.Services;
using static ArxSamples.DynamoDb.SampleOutput;
namespace ArxSamples.DynamoDb.Operations.RetrieveMultiple.Basics;
/// <summary>
/// Read many items with <see cref="ArxQueryExpression"/>, in three scenarios: a customer's orders, newest first and
/// filtered, a range of the sort key, and a query DynamoDB can't answer without a scan.
/// </summary>
/// <remarks>
/// A query needs the partition: an Equal condition on the attributes of the partition key (here the customer). Conditions
/// on the sort key's attribute narrow the range; the others filter what was read. Orders go by the sort key only. Without
/// a partition, ARX refuses (ARX8001) unless you allow a scan (Advanced 1).
/// </remarks>
public sealed class Basic1_RetrieveManyRecords : ISample
{
public string Name => "retrieve-multiple-basics-1-retrieve-many-records";
public string Title => "Retrieve many records";
public async Task RunAsync(SampleContext dbContext)
{
var orders = Enumerable.Range(1, 6).Select(i =>
{
var order = new ArxEntity("sales_order");
order["customer_id"] = "C-D411";
order["order_number"] = $"SO-D41{i}";
order["status"] = i % 3 == 0 ? "CANCELLED" : "OPEN";
order["ordered_on"] = $"2026-11-0{i}";
order["total"] = 10m * i;
return order;
}).ToList();
await dbContext.Service.CreateMultipleAsync(orders);
await Scenario1_ACustomersOrdersAsync(dbContext.Service);
await Scenario2_ARangeOfTheSortKeyAsync(dbContext.Service);
await Scenario3_NoPartitionAsync(dbContext.Service);
}
/// <summary>1. The customer's open orders, newest number first.</summary>
private static async Task Scenario1_ACustomersOrdersAsync(ArxDynamoDbService service)
{
Step("1. A customer's orders");
var query = new ArxQueryExpression("sales_order");
query.ColumnSet.AddColumns("order_number", "total");
query.Criteria.AddCondition("customer_id", ArxConditionOperator.Equal, "C-D411");
query.Criteria.AddCondition("status", ArxConditionOperator.Equal, "OPEN");
query.AddOrder("order_number", ArxOrderType.Descending);
var found = await service.RetrieveMultipleAsync(query);
Show(string.Join(", ", found.Entities.Select(x => $"{x["order_number"]} {x["total"]}")));
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT "order_number", "total" FROM "crm" WHERE "PK" = 'CUSTOMER#C-D411' AND begins_with("SK", 'ORDER#') AND "status" = 'OPEN'
* -- Expect: 4 items, SO-D411, SO-D412, SO-D414, SO-D415.
* ===========================================================
*/
}
/// <summary>2. Order numbers from SO-D413 on: a range of the sort key, read in the key's order.</summary>
private static async Task Scenario2_ARangeOfTheSortKeyAsync(ArxDynamoDbService service)
{
Step("2. A range of the sort key");
var query = new ArxQueryExpression("sales_order");
query.ColumnSet.AddColumns("order_number");
query.Criteria.AddCondition("customer_id", ArxConditionOperator.Equal, "C-D411");
query.Criteria.AddCondition("order_number", ArxConditionOperator.GreaterEqual, "SO-D413");
var found = await service.RetrieveMultipleAsync(query);
Show(string.Join(", ", found.Entities.Select(x => x["order_number"])));
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT "order_number" FROM "crm" WHERE "PK" = 'CUSTOMER#C-D411' AND "SK" >= 'ORDER#SO-D413'
* -- Expect: 4 items, SO-D413 to SO-D416.
* ===========================================================
*/
}
/// <summary>3. Every order over 30, whoever the customer: no partition, so ARX8001 rather than a hidden scan.</summary>
private static async Task Scenario3_NoPartitionAsync(ArxDynamoDbService service)
{
Step("3. No partition");
var query = new ArxQueryExpression("sales_order");
query.Criteria.AddCondition("total", ArxConditionOperator.GreaterThan, 30m);
try
{
await service.RetrieveMultipleAsync(query);
}
catch (ArxNotSupportedException exception)
{
ShowError(exception);
}
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT "order_number" FROM "crm" WHERE "entity_type" = 'sales_order' AND "total" > 30
* -- Expect: the orders over 30 (PartiQL scans the table for this).
* ===========================================================
*/
}
}
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. A customer's orders
SO-D415 50, SO-D414 40, SO-D412 20, SO-D411 10
> 2. A range of the sort key
SO-D413, SO-D414, SO-D415, SO-D416
> 3. No partition
caught ArxNotSupportedException: ARX8001 NotSupported: The query on 'sales_order' doesn't give its partition key (customer_id) with Equal, so DynamoDB could only answer it by reading the whole table (a Scan). Give the key, query an index whose key it gives, or pass new ArxDynamoReadOptions { AllowScan = true }.