-
Notifications
You must be signed in to change notification settings - Fork 0
Query and Scan
Query retrieves items by primary key with optional sort key conditions. Scan reads every item in a table or index. Both support filtering, projection, and pagination. See Querying and Scanning in the AWS docs.
Retrieves items matching a partition key and optional sort key condition.
Task<QueryResponse> QueryAsync(QueryRequest request, CancellationToken ct = default)- TableName (required) — target table
- KeyConditionExpression (required) — partition key equality + optional sort key condition
- FilterExpression — post-retrieval filter (doesn't reduce read cost)
- ProjectionExpression — comma-separated attributes to return
- ExpressionAttributeNames / ExpressionAttributeValues — expression substitutions
- IndexName — query a secondary index (see Secondary Indexes)
-
ScanIndexForward —
true(default) for ascending,falsefor descending sort key order - Limit — maximum number of items to evaluate (before filtering)
- ExclusiveStartKey — resume pagination from this key
-
Select —
ALL_ATTRIBUTES(default),COUNT, orALL_PROJECTED_ATTRIBUTES -
ConsistentRead — accepted but has no effect on table queries (always consistent). Throws
AmazonDynamoDBExceptionwhen set totrueon a GSI query.
The partition key must use equality (=). The sort key supports:
| Operator | Example |
|---|---|
= |
SK = :val |
< |
SK < :val |
<= |
SK <= :val |
> |
SK > :val |
>= |
SK >= :val |
BETWEEN ... AND ... |
SK BETWEEN :low AND :high |
begins_with |
begins_with(SK, :prefix) |
begins_with applies only to string sort keys (S). Numeric sort keys (N) do not support begins_with.
var response = await client.QueryAsync(new QueryRequest
{
TableName = "Orders",
KeyConditionExpression = "CustomerId = :cid AND OrderId BETWEEN :start AND :end",
ExpressionAttributeValues = new Dictionary<string, AttributeValue>
{
[":cid"] = new() { S = "customer-1" },
[":start"] = new() { S = "2024-01-01" },
[":end"] = new() { S = "2024-12-31" }
},
ScanIndexForward = false // newest first
});
foreach (var item in response.Items)
Console.WriteLine(item["OrderId"].S);QueryResponse? response = null;
do
{
response = await client.QueryAsync(new QueryRequest
{
TableName = "Orders",
KeyConditionExpression = "CustomerId = :cid",
ExpressionAttributeValues = new Dictionary<string, AttributeValue>
{
[":cid"] = new() { S = "customer-1" }
},
Limit = 25,
ExclusiveStartKey = response?.LastEvaluatedKey
});
// Process response.Items...
} while (response.LastEvaluatedKey is { Count: > 0 });| Field | Description |
|---|---|
Items |
List of matching items (null when Select.COUNT — guard with a null check) |
Count |
Number of items after filtering |
ScannedCount |
Number of items evaluated before filtering |
LastEvaluatedKey |
Key to resume pagination (null if no more results) |
Sort keys with ScalarAttributeType.N (number) are ordered numerically, not lexicographically. For example, 2 comes before 10, matching DynamoDB behavior. This is achieved via a separate sk_num column in SQLite.
When Select = Select.COUNT, the response contains Count and ScannedCount but Items is null (not an empty list — guard with a null check before iterating):
var response = await client.QueryAsync(new QueryRequest
{
TableName = "Orders",
KeyConditionExpression = "CustomerId = :cid",
ExpressionAttributeValues = new Dictionary<string, AttributeValue>
{
[":cid"] = new() { S = "customer-1" }
},
Select = Select.COUNT
});
Console.WriteLine($"Total: {response.Count}");Reads every item in a table or index, with optional filtering.
Task<ScanResponse> ScanAsync(ScanRequest request, CancellationToken ct = default)
// Legacy overloads
Task<ScanResponse> ScanAsync(string tableName, List<string> attributesToGet, CancellationToken ct = default)
Task<ScanResponse> ScanAsync(string tableName, Dictionary<string, Condition> scanFilter, CancellationToken ct = default)
Task<ScanResponse> ScanAsync(string tableName, List<string> attributesToGet, Dictionary<string, Condition> scanFilter, CancellationToken ct = default)- TableName (required) — target table
- FilterExpression — condition to filter results
- ProjectionExpression — attributes to return
- IndexName — scan a secondary index
- Limit — maximum items to evaluate
- ExclusiveStartKey — resume pagination
-
Select —
ALL_ATTRIBUTES,COUNT, orALL_PROJECTED_ATTRIBUTES - ExpressionAttributeNames / ExpressionAttributeValues — expression substitutions
- ConsistentRead — accepted but has no effect (always consistent)
var response = await client.ScanAsync(new ScanRequest
{
TableName = "Users",
FilterExpression = "Age > :minAge",
ExpressionAttributeValues = new Dictionary<string, AttributeValue>
{
[":minAge"] = new() { N = "21" }
},
ProjectionExpression = "UserId, #n, Age",
ExpressionAttributeNames = new Dictionary<string, string>
{
["#n"] = "Name"
}
});Scan pagination works identically to Query — use Limit and ExclusiveStartKey:
ScanResponse? response = null;
do
{
response = await client.ScanAsync(new ScanRequest
{
TableName = "Users",
Limit = 100,
ExclusiveStartKey = response?.LastEvaluatedKey
});
// Process response.Items...
} while (response.LastEvaluatedKey is { Count: > 0 });Pass IndexName to query or scan a secondary index:
var response = await client.QueryAsync(new QueryRequest
{
TableName = "Orders",
IndexName = "StatusIndex",
KeyConditionExpression = "#s = :status",
ExpressionAttributeNames = new Dictionary<string, string> { ["#s"] = "Status" },
ExpressionAttributeValues = new Dictionary<string, AttributeValue>
{
[":status"] = new() { S = "shipped" }
}
});See Secondary Indexes for details on index projection and behavior.
DynamoDbLite supports the legacy KeyConditions, QueryFilter, and ScanFilter dictionary-based APIs. These are automatically converted to expression-based equivalents internally.
For QueryFilter and ScanFilter: EQ, NE, LT, LE, GT, GE, BEGINS_WITH, CONTAINS, BETWEEN, NOT_NULL, NULL.
For KeyConditions only EQ, LT, LE, GT, GE, BEGINS_WITH, and BETWEEN are valid (matching DynamoDB constraints — NE and the rest are filter-only operators).
-
ConsistentReadis accepted but all reads are strongly consistent (SQLite behavior) -
ConsistentRead = trueon a GSI query throwsAmazonDynamoDBException— matches DynamoDB, which does not support consistent reads on global secondary indexes - Parallel scan:
TotalSegments/Segmentpartition results by FNV-1a hash of the partition key — each segment returns disjoint items. Segmentation is applied post-retrieval, so it does not reduce per-call work, andLimitapplies to the pre-segmentation row count (a call withLimit=100andTotalSegments=4returns roughly 25 items per segment, not 100). -
Limitapplies beforeFilterExpression(matches DynamoDB behavior) - TTL-expired items are automatically filtered out of results
-
LastEvaluatedKeyis returned whenever the number of evaluated items equalsLimit, including on the final page. Loop callers should expect an emptyItemson the next call when this happens.
Repo · NuGet · API Parity
Getting started
Reference
- Table Operations
- Item Operations
- Query and Scan
- Batch Operations
- Transactions
- Secondary Indexes
- TTL
- Tags and Admin
- DI and Configuration
- Concurrency
- Performance
- API Parity
- FAQ
Recipes
- DynamoDBContext for tests
- xUnit per-test isolation
- ASP.NET Core integration test fixture
- Migrating tests off DynamoDB Local
Internals