Supported Operators¶
This page is the authoritative reference for which LINQ operators translate to PartiQL server-side, which are evaluated client-side, and which are unsupported.
Filtering Operators¶
| LINQ Operator | PartiQL Translation | Notes |
|---|---|---|
Where |
WHERE |
Boolean properties normalized to = true |
==, !=, scalar Equals |
=, <> |
Works on all scalar types; supports single-argument prop.Equals(value), string.Equals(value, StringComparison.Ordinal), static string.Equals, and object.Equals(prop, value) for compatible scalar operands; complex property-to-property and parameter equality translate for map attributes; comparers and non-ordinal StringComparison overloads are unsupported |
<, <=, >, >= |
<, <=, >, >= |
Numeric and date/time properties only; C# does not define these operators on string — use string.Compare or CompareTo instead |
string.Compare(a, b) > 0 (any comparison against 0) |
a > b |
Required for string range comparisons; works with ==, !=, <, <=, >, >= against 0 only. Exact comparisons against 1 or -1 are not translated because .NET only guarantees negative/zero/positive results |
a.CompareTo(b) > 0 (any comparison against 0) |
a > b |
Same as above; 0 < a.CompareTo(b) form also supported (operands mirrored). Compare only against 0 |
== null |
IS NULL OR IS MISSING |
Covers both DynamoDB null representations, including nested complex-property paths such as x.Profile.Address == null |
!= null |
IS NOT NULL AND IS NOT MISSING |
De Morgan inverse of == null; also works on nested complex-property paths |
nullable scalar/complex HasValue |
IS NOT NULL AND IS NOT MISSING |
Checks that the scalar attribute or complex map attribute exists and is not NULL |
nullable complex .Value.Property |
nested attribute path | Accesses scalar members inside nullable complex value objects, e.g. x.Profile.Value.ZipCode → "Profile"."ZipCode" |
EF.Functions.IsNull(prop) |
prop IS NULL |
Explicit: NULL type only |
EF.Functions.IsNotNull(prop) |
prop IS NOT NULL |
Explicit: not NULL type |
EF.Functions.IsMissing(prop) |
prop IS MISSING |
Explicit: attribute absent from item |
EF.Functions.IsNotMissing(prop) |
prop IS NOT MISSING |
Explicit: attribute present |
!expr |
NOT (expr) |
Operand always parenthesized |
prop >= a && prop <= b |
prop BETWEEN a AND b |
Both bounds must be inclusive (>= and <=) |
string.Contains(s) |
contains(attr, ?) |
Case-sensitive substring; supports string and StringComparison.Ordinal overloads; char, culture, and non-ordinal comparison overloads are unsupported |
entity.Collection.Contains(value) |
contains(attr, ?) |
Native DynamoDB list/set primitive collection membership; scalar value-converted collections are not translated |
entity.Collection.Any() / entity.Bytes.Any() |
size(attr) > 0 |
Native DynamoDB primitive collection and binary (byte[]) attributes only; predicate Any(...) remains unsupported except inline finite collection rewrites |
entity.Collection.Count, .Count(), .Length; entity.Bytes.Length |
size(attr) |
Native DynamoDB primitive collection length/count and binary byte length; predicate Count(...) and query-level aggregate Count remain unsupported |
entity.Collection[constant], .ElementAt(constant) |
attr[index] |
Direct primitive LIST element access with a constant numeric index only; dynamic indexes, filtering, ordering, or paging inside list elements remain unsupported |
entity.Collection.First(), .FirstOrDefault() |
attr[0] |
Direct first-element access on primitive LIST attributes only; translates as constant index 0. General sequence First* terminal rules still apply to root queries |
string.StartsWith(s) |
begins_with(attr, ?) |
Case-sensitive prefix; supports string and StringComparison.Ordinal overloads; char, culture, and non-ordinal comparison overloads are unsupported |
string.Length |
size(attr) |
DynamoDB size semantics; for strings, use with care when non-ASCII text matters |
string.IsNullOrEmpty(s) |
attr IS NULL OR attr IS MISSING OR attr = '' |
Matches DynamoDB NULL, missing attributes, or empty string |
entity.Bytes.SequenceEqual(bytes) |
attr = ? |
Binary (byte[]) equality; binary literals are parameterized because DynamoDB PartiQL has no inline binary literal syntax |
collection.Contains(prop) |
prop IN [?, ...] |
In-memory collection membership; max 50 PK values, 100 non-key values. Nullable item comparisons are supported when element mapping is known |
new[] { a, b }.Any(x => x == prop) |
(a = prop) OR (b = prop) |
Inline finite collection predicate rewrite only; no server aggregate or subquery is emitted |
new[] { a, b }.All(x => x != prop) |
(a <> prop) AND (b <> prop) |
Inline finite collection predicate rewrite only; useful for negated contains-style predicates |
OfType<TDerived>() |
discriminator predicate | TPH inheritance only; filters by concrete discriminator values for the requested derived type. x is TDerived translates when the target type can be expressed as discriminator values. x.GetType() == typeof(TConcrete) translates only for exact concrete mapped entity types. |
Projection Operators¶
| LINQ Operator | PartiQL Translation | Notes |
|---|---|---|
Select |
Explicit column list | SELECT * is never emitted; computed expressions and nested complex-property shaping evaluate client-side. Primitive collection properties can be projected directly |
Ordering Operators¶
| LINQ Operator | PartiQL Translation | Notes |
|---|---|---|
OrderBy / OrderByDescending |
ORDER BY col ASC\|DESC |
Only partition key and sort key columns are valid |
ThenBy / ThenByDescending |
Additional ORDER BY column |
For multi-partition queries, partition key must be the first column |
Terminal Operators¶
Terminal operators execute the query and return results. The provider supports the following terminals:
| LINQ Operator | Behavior | Notes |
|---|---|---|
ToListAsync() |
Fetches all pages; returns List<T> |
Provider follows NextToken until exhausted |
AsAsyncEnumerable() |
Streams results as IAsyncEnumerable<T> |
Marks the explicit client-side evaluation boundary; LINQ applied after this runs in-process |
ToPageAsync(limit, token) |
Single DynamoDB request; returns DynamoPage<T> |
DynamoDB-specific; use for cursor-based pagination |
First / FirstOrDefault |
Implicit Limit=1; single request |
Key-only safe path by default; AsUnsafeFilteredQuery() bypasses safety validation but can miss later matching items |
Single / SingleOrDefault |
Implicit Limit=2; single request |
Key-condition-only; allows PK equality or PK IN; shared-table discriminator queries need base-table single-item lookup |
Primary-Key Lookup¶
Use FindAsync when you have the full DynamoDB table key and want one entity by primary key. It
checks the EF Core change tracker first. If the entity is not already tracked, the provider executes
a base-table key-equality PartiQL query with ExecuteStatementRequest.Limit = 1.
// Partition-key-only table
var customer = await context.Customers.FindAsync(["CUSTOMER#123"], ct);
// Composite partition + sort key table; pass values in PK, SK order
var order = await context.Orders.FindAsync(["CUSTOMER#123", "ORDER#456"], ct);
FindAsync returns null when no item exists. It targets the base table and does not use secondary
indexes or automatic index selection; use normal LINQ with .WithIndex(...) or automatic selection
for secondary-index lookups. Synchronous Find can return an already-tracked entity, but throws if
it needs to query DynamoDB.
DynamoDB-Specific Extensions¶
The following extension methods are unique to this provider and have no standard LINQ equivalent. They compose with the query before a terminal operator is called.
| Extension | Behavior | Notes |
|---|---|---|
Limit(n) |
Sets ExecuteStatementRequest.Limit — evaluation budget |
DynamoDB reads up to n items then applies filters; returned count is 0..n. Last call wins. See Ordering and Limiting |
WithNextToken(token) |
Seeds the first request with a saved continuation cursor | Resumes a previous query from a known position. See Pagination |
WithConsistentRead() |
Requests strongly consistent reads for this query | Sets ExecuteStatementRequest.ConsistentRead = true for base-table and LSI reads. Throws if the finalized source is a GSI because DynamoDB does not support strong consistency on GSIs. WithConsistentRead(false) explicitly uses eventually consistent reads for one query |
WithIndex(name) |
Routes the query to a named GSI or LSI | Emits FROM "Table"."Index" in PartiQL. See Index Selection |
WithoutIndex() |
Forces base-table execution; suppresses automatic index selection | Emits diagnostic DYNAMO_IDX006. Cannot combine with WithIndex(...) |
AsUnsafeFilteredQuery() |
Bypasses First* safety validation for this query |
Does not disable scan-like query protection, does not bypass Single* validation, and does not change terminal execution semantics. Use only for tests or controlled legacy code |
Unsupported Operators¶
The following operators are not supported and throw InvalidOperationException at translation time. The provider does not silently fall back to in-process evaluation.
| Category | Operators | Reason |
|---|---|---|
| Element operators | Any(predicate), All except supported inline finite collection predicate rewrites |
General server-side element operators are unsupported; primitive collection Any() translates to size(attr) > 0 |
| Offset / paging | Skip, Take, root/query-sequence ElementAt, ElementAtOrDefault |
DynamoDB has no offset semantics; direct primitive LIST attribute ElementAt(constant) translates to list indexing |
| Reverse traversal | Last, LastOrDefault, Reverse |
Requires reverse index traversal, not currently implemented |
| Deduplication | Distinct |
SELECT DISTINCT is not supported in DynamoDB PartiQL |
| Aggregation | Count, LongCount, Sum, Average, Min, Max |
No query aggregate functions in DynamoDB PartiQL; primitive collection count/length translates to size(attr) |
| Grouping | GroupBy |
GROUP BY is not supported |
| Joins | Join, GroupJoin, SelectMany, LeftJoin, RightJoin, DefaultIfEmpty |
DynamoDB does not support cross-item joins |
| Set operations | Union, Concat, Except, Intersect |
Not supported |
| Conditional filtering | SkipWhile, TakeWhile |
Not supported |
| Type casting | Cast<T> |
Not supported; use TPH inheritance queries with OfType<TDerived>() for type filtering |
Scalar Type Support¶
All scalar entity properties must map to a DynamoDB attribute type. Types without a native wire representation use built-in EF Core value converters.
| .NET Type | DynamoDB Attribute Type | Wire Format |
|---|---|---|
string |
S |
— |
bool |
BOOL |
— |
byte, sbyte, short, int, long |
N |
Numeric string |
ushort, uint, ulong |
N |
Numeric string |
float, double, decimal |
N |
Numeric string |
byte[] |
B |
Binary |
Guid |
S |
"D" format, e.g. "550e8400-e29b-41d4-a716-446655440000" |
DateTime |
S |
ISO 8601 round-trip ("O") |
DateTimeOffset |
S |
ISO 8601 round-trip ("O") |
DateOnly |
S |
"yyyy-MM-dd", e.g. "2026-04-19" |
TimeOnly |
S |
"HH:mm:ss" (whole-second) or "o" (sub-second) |
TimeSpan |
S |
Constant ("c") format, e.g. "01:30:00" |
| Enum | N |
Underlying numeric value (string names require a converter) |
Nullable variants of all types above are supported. Numeric comparisons use DynamoDB's single N wire type, so promotions to int, such as short to int, still bind as DynamoDB numbers. Custom types can be mapped using EF Core value converters; query parameters and constants use the property mapping so converters apply consistently.
Value-converted enums compare using the converter provider type. For example, an enum mapped with .HasConversion<string>() binds parameters as DynamoDB strings. Numeric casts of value-converted enums, such as (int)entity.Status == value, are not translated for parameter comparisons because the DynamoDB attribute stores the converted provider value. Compare enum values directly, or map the enum numerically if numeric comparisons are required.