# Query<T>

> Holds a query instead of its rows. A clause you write against a `Query<T>` joins the query rather than filtering rows already fetched, so a chain split across a binding — or across a function boundary — is still ONE SQL statement. Without it, splitting the chain reads every matching row and finishes the work in memory.

<!-- id: query-deferred · area: query · stability: stable · html: https://osysharp.com/reference/query/deferred/ -->

## Summary        {#summary}
A `Query<T>` is a query **that has not run yet** — and it is what a LINQ chain already is, whether or not you spell
it. Writing the type down matters in one place: when the query has to cross a function boundary, where a parameter
needs a type.

```osy title="one statement, written in two pieces" test app=query-deferred
entity Film {
  [Required, MaxLength(200)] string Title;
  int Year;
  decimal Rating;

  security { allow create, read, update when IsAuthenticated || IsAnonymous; }
}

string BestOfYear(int year) {
  Query<Film> candidates = Film.Where(f => f.Year == year);       // the filter …
  return candidates.OrderByDescending(f => f.Rating).First().Title;  // … and the pick, in ONE query
}
```

## Signature      {#signature}
```osy syntax
var         q = Film.Where(…);          // inferred — holds the query, does not run it
Query<Film> q = Film.Where(…);          // the same thing, written down
Film Best(Query<Film> q) { … }          // a parameter — the caller supplies the query
q.OrderBy(…).Take(…)                    // a clause JOINS the query
foreach (var f in q) { … }              // read as rows: HERE the query runs
q.ToList()                              // …and this is how you say "run it now" on purpose
```

## Description    {#description}

### C# spells it `IQueryable<T>`, and that works too   {#iqueryable}
If you reach for C#'s name, write it — `IQueryable<Film>` **is** `Query<Film>`, in every position, and there is no
conversion or preference between them:

```osy title="the C# spelling, doing exactly the same thing" test app=query-deferred
Film PickBestOf(IQueryable<Film> candidates) { return candidates.OrderBy(f => f.Rating).First(); }
```

⚠ Its SIBLING interfaces are a different answer, and the reason is worth knowing: `IEnumerable<T>`,
`IReadOnlyList<T>`, `IList<T>` are refused, because Osy# spells those `T[]` and `List<T>` — **which are themselves
C#**, so the refusal leaves you writing C# and costs one round trip. `IQueryable<T>` has no such alternative
spelling, so refusing it would push you off C# onto an Osy#-only word. That is why one is accepted and the others
are taught.

### A plain `var` is the same thing   {#var}
You do not have to write the type. `var` infers it, exactly as `var q = db.Orders.Where(…)` infers `IQueryable` in
C#, so a chain split across a binding is still one statement:

```osy title="`var` and the written type are the same query" test app=query-deferred
// Both are ONE statement: WHERE, ORDER BY and LIMIT 1 together.
string BestInferred(int year) {
  var candidates = Film.Where(f => f.Year == year);
  return candidates.OrderByDescending(f => f.Rating).First().Title;
}

string BestSpelled(int year) {
  Query<Film> candidates = Film.Where(f => f.Year == year);
  return candidates.OrderByDescending(f => f.Rating).First().Title;
}
```

### What ends the deferral — `.ToList()`, and a terminal   {#ending}
The chain is deferred until something asks for the answer. Two things do, and they are how you say "read it here":

```osy title="`.ToList()` is `run it now`" test app=query-deferred
// `.ToList()` reads the rows HERE. The sort below runs over the list, in memory — which is what you want when
// you are going to ask the same rows several questions.
string BestThenCount(int year) {
  var rows = Film.Where(f => f.Year == year).ToList();
  var best = rows.OrderByDescending(f => f.Rating).First().Title;
  return best + " of " + rows.Count.ToString();
}
```

A **terminal** — `First`, `Single`, `Count`, `Any`, `Sum` and friends — ends it too, because it has produced the
answer. And a **declared row type** asks for the rows at the binding: `Film[] rows = Film.Where(…);`.

### Passing a query to a function   {#across-functions}
A `Query<T>` parameter is the half that has no other spelling: the caller decides **which** rows, the helper decides
what to **do** with them, and it is still one statement.

```osy title="the caller filters, the helper orders and pages" test app=query-deferred
string[] TopTitles(Query<Film> src, int take) {
  return src.OrderByDescending(f => f.Rating).Take(take).Select(f => f.Title);
}

string[] BestOf(int year) { return TopTitles(Film.Where(f => f.Year == year), 3); }
string[] BestEver()       { return TopTitles(Film.Where(f => f.Rating > 0m), 10); }
```

The helper is **composed into** each call rather than called, so its body must be a single `return <query>;` (or an
`=> <query>` expression body). A body that does more than that has no expression to compose, and says so.

### Reading the rows   {#reading-rows}
Used anywhere rows are wanted — a `foreach`, a `return`, an argument — a `Query<T>` **is** the rows, and that is
where it runs. It is only special at the head of a chain.

```osy title="the same binding, read as rows" test app=query-deferred
int CountOfYear(int year) {
  Query<Film> candidates = Film.Where(f => f.Year == year);
  return candidates.Count();                    // SELECT count(*) … WHERE Year = @year
}

string[] TitlesOfYear(int year) {
  Query<Film> candidates = Film.Where(f => f.Year == year);
  var titles = new List<string>();
  foreach (var f in candidates) { titles.Add(f.Title); }   // runs here
  return titles.ToArray();
}
```

### Using one twice runs it twice   {#twice}
⚠ **This is the one thing to know, and it applies to the inferred `var` as much as to the written type.** A deferred
query is a description, not a result — so each use goes to the database, exactly as enumerating a C# `IQueryable`
twice is two round trips. When you want the rows once and then several answers from them, say so with
[ToList](https://osysharp.com/reference/query/tolist/):

```osy title="two answers from one read" test app=query-deferred
string Report(int year) {
  var rows = Film.Where(f => f.Year == year).ToList();   // read ONCE …
  var howMany = rows.Count();                            // … then ask it twice, in memory
  var best = rows.Max(f => f.Rating);
  return howMany.ToString() + " films, best " + best.ToString();
}
```

### What it will not hold   {#refusals}
The initializer has to be a query that has not run. The two ways to get that wrong are different mistakes, and each
is named:

| You wrote | Why it is refused |
|---|---|
| `Query<Film> q = Film.Where(…).First();` | `First` **runs** it — what you have is the answer, not the query. Write the terminal where you use it. |
| `Query<Film> q = rows.Where(…);` over a `Film[]` | those rows have already been read. To hold rows, declare the list type: `Film[]`, `List<Film>`. |
| `Query<Film> t = Film.Select(f => f.Title);` | the query yields `string`. The declared element is checked against what the query **yields**, which a projection changes — write `Query<string>`. |

### Why it is not a value you can store   {#compile-time}
A `Query<T>` is resolved where it is used, at compile time — it is not an object that exists while the program runs,
so it cannot be put in a field, returned from a function, or held in a list. That is a **security** boundary before
it is an economy: a query becomes SQL, and a query that could travel as a value could travel to a function running
in the browser. Composing in the compiler means nothing new crosses the wire.

To hand rows to something that outlives the expression, materialise them with [ToList](https://osysharp.com/reference/query/tolist/).

### Do the write terminals compose too?   {#write-terminals}
Yes — a `Query<T>` may end in [`.Delete()`](https://osysharp.com/reference/query/delete/), [`.Update(…)`](https://osysharp.com/reference/query/update/) or
[`.Insert(…)`](https://osysharp.com/reference/query/insert-from/) exactly as it ends in `.Count()`: the helper's chain composes into the caller's
statement at compile time, so `int PurgeVia(Query<Order> doomed) { return doomed.Delete(); }` is still one
statement, with the caller's security floor.

## See also       {#see-also}
- [Where / Single / Count](https://osysharp.com/reference/query/where/) — the clauses a `Query<T>` composes
- [ToList](https://osysharp.com/reference/query/tolist/) — reading the rows once, when you want several answers from them
- [Skip / Take (paging)](https://osysharp.com/reference/query/paging/) — `Skip` / `Take`, the clause most worth composing rather than filtering
- [LINQ over a local list](https://osysharp.com/reference/query/in-memory-linq/) — the other side: LINQ over rows you already hold
