DynamicWhere.ex
DynamicWhere.exv3.4.0·docs

Operator

Operator declares the comparison applied by a Condition. There are 28 operators total. Every operator that starts with I is the case-insensitive variant — both sides are normalized with .ToLower() before comparison.

Required values at a glance

Each operator expects a specific number of entries in Condition.Values. Sending the wrong count throws a LogicException whose message is one of ConditionWithOperator[<op>]MustHasOnlyOneValue, ConditionWithOperator[Between-NotBetween]MustHasOnlyTwoValues, ConditionWithOperator[In-IIn-NotIn-INotIn]MustHasOneOrMoreValues, or ConditionWithOperator[IsNull-IsNotNull]MustHasNoValues. There is no error-code property — the string is the message.

Value countOperators
0IsNull, IsNotNull
1All equality, contains, starts-with, ends-with, and ordered comparisons (20 operators total — the 28 minus the two null checks, the two range checks, and the four In variants).
2 (exactly)Between, NotBetween
1+In, IIn, NotIn, INotIn

Equality

OperatorDescriptionRequired Values
EqualEquality (case-sensitive for text).1
IEqualEquality (case-insensitive).1
NotEqualInequality (case-sensitive).1
INotEqualInequality (case-insensitive).1

Contains / StartsWith / EndsWith

OperatorDescriptionRequired Values
ContainsText contains (case-sensitive).1
IContainsText contains (case-insensitive).1
NotContainsText does not contain (case-sensitive).1
INotContainsText does not contain (case-insensitive).1
StartsWithStarts with (case-sensitive).1
IStartsWithStarts with (case-insensitive).1
NotStartsWithDoes not start with (case-sensitive).1
INotStartsWithDoes not start with (case-insensitive).1
EndsWithEnds with (case-sensitive).1
IEndsWithEnds with (case-insensitive).1
NotEndsWithDoes not end with (case-sensitive).1
INotEndsWithDoes not end with (case-insensitive).1
Note
Case-insensitive I* operators emit .ToLower() on both sides of the comparison. On SQL Server this is typically free (default collations are case-insensitive). On case-sensitive collations (e.g. PostgreSQL with C locale) this still works but may sidestep an index.

In / NotIn (set membership)

OperatorDescriptionRequired Values
InValue is in the set (case-sensitive for text).1+
IInValue is in the set (case-insensitive).1+
NotInValue is not in the set (case-sensitive).1+
INotInValue is not in the set (case-insensitive).1+
Long lists
A list of up to 32 values is written as one chain of comparisons, and a longer one as a balanced tree of such chains, which returns the same rows. Before 3.1.0 every list was one chain, and a single condition carrying about seven hundred values could overflow the request thread's stack and end the process — see breaking changes. Under ApplyPolicy, MaxConditionValues (default 1000) bounds the values one condition may carry.

Ordered comparisons & ranges

OperatorDescriptionRequired Values
GreaterThanGreater than.1
GreaterThanOrEqualGreater than or equal.1
LessThanLess than.1
LessThanOrEqualLess than or equal.1
BetweenInclusive range — first value is the lower bound, second is the upper bound.2 (exactly)
NotBetweenOutside the inclusive range defined by the two values.2 (exactly)

Null checks

OperatorDescriptionRequired Values
IsNullProperty is NULL.0
IsNotNullProperty is NOT NULL.0
On a date member that cannot be null
With DataType.Date or DataType.DateTime, the library reads the member's type first. A non-nullable DateTime, DateTimeOffset or DateOnly of the entity itself can never be null, so IsNull answers the constant false — no rows — and IsNotNull the constant true — every row. On PostgreSQL that is WHERE FALSE and no predicate at all. Reached through a navigation, as in Approval.ApprovedAt, they test the navigation instead: IsNull matches the rows with no Approval. On a nullable date member they test the column as usual. See breaking changes.
Warning
Sending any value with IsNull / IsNotNull throws ConditionWithOperator[IsNull-IsNotNull]MustHasNoValues. Send an empty array: "values": [].

JSON examples

Range (Between):

{
  "sort": 1,
  "field": "Price",
  "dataType": "Number",
  "operator": "Between",
  "values": [100, 500]
}

Set membership (IIn):

{
  "sort": 1,
  "field": "Country",
  "dataType": "Text",
  "operator": "IIn",
  "values": ["USA", "Canada", "UK"]
}

Null check (IsNull):

{
  "sort": 1,
  "field": "DeletedAt",
  "dataType": "DateTime",
  "operator": "IsNull",
  "values": []
}