Queries

db.Query() provides a fluent interface for filtering, sorting, pagination, aggregation, and query-scoped writes over the full dataset. Every db.Query() call is a full scan — use QueryViaIndex when an index lookup is more appropriate.

Methods fall into two categories:

  • Builder methods — shape the working set and chain together: Where, OrderBy, Skip, Limit
  • Terminal methods — execute and return a result: ToArray, Select, SelectDistinct, Sum, Min, Max, Avg, Update, Delete

GroupBy is a special case — it transitions into a grouped query context. See GroupBy.

Builder Methods

MethodSignatureDescription
WhereWhere(Func<T, bool>)Filter records by predicate
OrderByOrderBy(Comparison<T>)Sort using a comparison delegate
SkipSkip(int)Skip the first N records
LimitLimit(int)Cap the result set at N records
GroupByGroupBy<TKey>(Func<T, TKey>)Group records by key — see GroupBy

OrderBy takes a Comparison<T> delegate — the same contract as Array.Sort. Ascending compares p1 to p2; descending reverses the arguments and compares p2 to p1.

// Filter, sort, paginate
Person[] page = db.Query()
    .Where(p => p.IsAdult)
    .OrderBy((p1, p2) => p1.BirthDate.CompareTo(p2.BirthDate))
    .Skip(5)
    .Limit(5)
    .ToArray();

Materialization

// Materialize all matching records as T[]
Person[] kids   = db.Query().Where(p => !p.IsAdult).ToArray();
Person[] adults = db.Query().Where(p => p.IsAdult).ToArray();

// Project to a different type — returns string[]
string[] names = db.Query()
    .Where(p => p.Ssn != string.Empty)
    .Select(p => $"{p.LastName}, {p.FirstName}");

// Project to distinct values, sorted — returns string[]
string[] distinctLastNames = db.Query()
    .Where(p => p.Ssn != string.Empty)
    .OrderBy((p1, p2) => p1.LastName.CompareTo(p2.LastName))
    .SelectDistinct(p => p.LastName);

Select and SelectDistinct skip the deep copy when the projected type is a string or value type — only reference-type projections incur the clone cost.

Aggregates

All aggregates return 0 or default(Y) when the result set is empty.

// Sum
decimal totalKidNetWorth   = db.Query().Where(p => !p.IsAdult).Sum(p => p.NetWorth);
decimal totalAdultNetWorth = db.Query().Where(p => p.IsAdult).Sum(p => p.NetWorth);

// Min / Max
decimal minKidNetWorth   = db.Query().Where(p => !p.IsAdult).Min(p => p.NetWorth);
decimal maxAdultNetWorth = db.Query().Where(p => p.IsAdult).Max(p => p.NetWorth);

// Avg — combine multiple Where conditions with &&
decimal avgKidNetWorth          = db.Query().Where(p => !p.IsAdult).Avg(p => p.NetWorth);
decimal avgHighCreditAdultWorth = db.Query()
    .Where(p => p.IsAdult && p.CreditScore >= 700)
    .Avg(p => p.NetWorth);

Sum is overloaded for the standard numeric types and returns the same type as the selector. Min and Max are generic — they return whatever type the selector projects to.

Selector typeSum returnsAvg returns
intintdouble
longlongdouble
floatfloatfloat
doubledoubledouble
decimaldecimaldecimal

Query-Scoped Writes

Query-scoped Update and Delete apply one action across every record matching the query and return the count affected. They operate identically to their Basic Operations counterparts and remain full scans.

// Increase credit score by 10 for high-net-worth adults — returns count updated
int updated = db.Query()
    .Where(p => p.IsAdult && p.CreditScore > 0 && p.NetWorth >= 100_000)
    .Update(p => p.CreditScore = p.CreditScore + 10);

// Delete the 2 adults with the lowest credit scores — returns count deleted
int deleted = db.Query()
    .Where(p => p.IsAdult)
    .OrderBy((p1, p2) => p1.CreditScore.CompareTo(p2.CreditScore))
    .Limit(2)
    .Delete();

GroupBy

GroupBy transitions into a grouped query context and exposes two operations:

  • Having — filter groups by predicate; chainable
  • Select — terminal; projects each group to a result and returns TResult[]
// Sum net worth per family (group by last name)
(string lastName, decimal netWorth)[] familyWorth = db.Query()
    .GroupBy(p => p.LastName)
    .Select(g => (g.Key, g.Sum(p => p.NetWorth)));

// Same query, but only families with more than one member
(string lastName, decimal netWorth)[] multiMemberFamilies = db.Query()
    .GroupBy(p => p.LastName)
    .Having(g => g.Count() > 1)
    .Select(g => (g.Key, g.Sum(p => p.NetWorth)));

The Select lambda receives a standard IGrouping<TKey, T> — all standard LINQ operations are applicable.