Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Query

Select (projections)

<Entity>.Select(o => o.Column) · .Select(o => new T { A = o.X, … }) · .Select(o => new { o.X, o.Y })

Reshape what a query returns: one column, an anonymous row, or a `class` you declared. The projection becomes the SQL SELECT list, so the columns you did not ask for are never read — which is the point of it. `Select` must be the LAST clause of the chain; only `Where`, `OrderBy` and `Take` may precede it, and only `Distinct()` may follow.

stable3 examples compiled by CIquerydataprojection

Summary#

Select says which columns you want, and in what shape:

class OrderRow {
  public string Code;
  public decimal Total;
}

entity Order {
  [Required, MaxLength(40)] string Code;
  [MaxLength(60)] string Region;
  decimal Total;
  bool Cancelled;
}

List<string> Codes() {
  return Order.Select(o => o.Code).ToList();               // one column → a List<string>
}

List<OrderRow> Rows() {
  return Order.Where(o => !o.Cancelled)
              .Select(o => new OrderRow { Code = o.Code, Total = o.Total })
              .ToList();                                                       // → a class you declared
}

The projection becomes the SQL SELECT list, so the columns you did not name are never read — no wide rows over the wire, and no entity to materialise.

Signature#

<Query>.Select(o => o.Column)              // a scalar     → List<string> / List<decimal> / …
<Query>.Select(o => o.Total * 2)           // an expression scalar
<Query>.Select(o => new T { A = o.X, … })  // a `class` you declared → List<T>
<Query>.Select(o => new { o.X, o.Y })      // anonymous — usable on the spot, cannot be returned

Description#

It must be the last clause#

A Select ends the chain. The compiler enforces a narrow window around it, and the restrictions are not arbitrary — each one is a thing SQL cannot do once the rows have been reshaped:

Before a Select, only: Where · OrderBy / OrderByDescending · Take.

  • ThenBy may not precede a Select. Sort the projected result instead.
  • Skip may not precede a Select (though Take may). To page a projection: project into a class and page that, or page the entity query and project after.
  • Include may not precede a Select — and it would be meaningless if it could: Include pre-loads related entity rows, and a projection does not return entity rows at all. Just select what you want.

After a Select, only: ToList() (a no-op — the projection already materialises) and Distinct().

There are no terminals over a projection: no First(), no Count(), no Sum(). Do the aggregate over the entity query instead (Sum / Average / Min / Max / Count), or group it (GroupBy (and HAVING)).

The one exception: Distinct().Count()#

The one composition allowed after a projection, because it is a single SQL expression — and it answers a question you genuinely cannot get another way:

int RegionsSoldInto() {
  return Order.Select(o => o.Region).Distinct().Count();   // → COUNT(DISTINCT region)
}

Distinct() over a scalar projection then Count() becomes COUNT(DISTINCT col). It must be the last clause, and the projection must be a scalar. See Distinct.

Which shape to reach for#

  • A scalar (o => o.Code) when you want a list of values — ids to pass on, codes to render, amounts to sum in code. You get a real List<T>.
  • A class when you want rows with names, especially across a function boundary. This is the workhorse: declare the shape, project into it, return it. It is a plain data shape (class methods) — no entity, no tracking, no lazy loading, nothing to surprise you later. A class projection can also back a reactive live var in a component — a live list of the shape you render, refreshing on commit (The reactivity & lifecycle model).
  • Anonymous (o => new { o.Code, o.Total }) only where the result is consumed on the spot. It has no name, so it cannot be a return type.

It does not change what security allows#

A projection narrows the columns, never the rows. The read rules are compiled into the same statement, so Select cannot be used to see a row you were not granted — and a field mask (deny read PasswordHash when …) still applies to the column you projected. Projecting is a performance and shape decision, not an access one.

Examples#

Projecting a computed value, and a narrow row for a list view — the common case, and the one that keeps a grid fast:

class OrderCard {
  public string Code;
  public decimal Total;
  public decimal WithVat;
  public bool Big;
}

List<OrderCard> Cards(decimal bigFrom) {
  return Order.Where(o => !o.Cancelled)
              .OrderByDescending(o => o.Total)
              .Take(50)
              .Select(o => new OrderCard {
                Code    = o.Code,
                Total   = o.Total,
                WithVat = o.Total * 1.2m,        // computed in the database
                Big     = o.Total > bigFrom })   // a captured local works, like any parameter
              .ToList();
}

Where did my row come in the order?#

Select((s, i) => …) — C#'s index-aware projection — works over stored rows once the chain names an order: i is the row's 0-based position in that order, computed by the database. The partitioned forms (per-group ranks, the previous row's value) are the window functions.

See also#

Related

Querying data

How you read data in Osy#. You write C# LINQ; it becomes one SQL statement. The rules that follow from that are the…

Sum / Average / Min / Max / Count

Fold rows down to a single number — a query or a `List<T>` you already hold. The one thing to know before you use them:…

GroupBy (and HAVING)

Group rows by a key and reduce each group to one row — `g.Key`, `g.Count()`, `g.Sum/Average/Min/Max(…)` — computed by…

Distinct

Removes duplicate rows from a query result. On whole entity rows it is a no-op, because rows are already unique by Id —…

Where / Single / Count

Query an entity by writing a predicate over it. The query runs in the database — not a filter over rows you already…

LINQ over a local list

Query a local `List<T>`, `HashSet<T>` or `T[]` — of your own `class` values OR of plain scalars like `string[]` and…

class methods

Behaviour attached to a class — a method with a receiver, called as value.Method(). Classes are in-memory values, so a…

The reactivity & lifecycle model

How an Osy# component comes alive and stays in sync: declarations are live value bindings, `on mount`/`on unmount` are…