# ToList

> Runs the query and materialises the rows as a `List<Entity>`. Until you call it, a query is a description of what you want; ToList is the moment it becomes rows you can walk, count and index. `.ToArray()` does the same and answers the fixed-size `Entity[]` instead.

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

## Summary        {#summary}
`.ToList()` **runs** the query. Up to that point you have built a description — a set of conditions, an order, a page
— and nothing has touched the database. `ToList` is where it executes and you get rows back, typed as `List<Entity>`
exactly as in C#.

## Signature      {#signature}
```osy syntax
<Entity>.Where(…).OrderBy(…).ToList()    →   List<Entity>    // materialised, and mutable
<Entity>.Where(…).OrderBy(…).ToArray()   →   <Entity>[]      // materialised, and fixed-size
<Entity>.Where(…).OrderBy(…)             →   <Entity>[]      // a chain that simply ends
```

## Description    {#description}

### When does the query actually run?   {#building}
The chain composes without executing. Only `ToList` (and the scalar calls — `Count`, `Any`, `Single`) go to the
database:

```osy title="compose, then run once" test app=query-tolist
entity Order {
  [Required] string Code;
  decimal Total;
}

List<Order> TopOrders(decimal min, int take) {
  return Order
    .Where(o => o.Total >= min)      // nothing has run yet
    .OrderByDescending(o => o.Total) // still nothing
    .Take(take)
    .ToList();                       // NOW it runs — one query, one round trip
}
```

### What you get back   {#the-result}
A `List<Entity>` — materialised rows. It has a `.Count`, it can be indexed, and it can be walked:

```osy title="using the result" test app=query-tolist
decimal SumOfTop(decimal min, int take) {
  var top = TopOrders(min, take);

  var total = 0m;
  foreach (var o in top) { total += o.Total; }   // walk it

  var first = top.Count > 0 ? top[0].Total : 0m;  // index it
  return total + first * 0m;
}
```

### Why does calling it twice do the work twice?   {#run-once}
Because `ToList` is the moment work happens, calling it twice does the work twice. Materialise into a local and use
that:

```osy title="materialise once, use many times" test app=query-tolist
string Describe(decimal min) {
  var orders = Order.Where(o => o.Total >= min).ToList();   // one query
  var count = orders.Count;
  var total = 0m;
  foreach (var o in orders) { total += o.Total; }
  return Convert.ToString(count) + " orders, " + Convert.ToString(total);
}
```

Writing `Order.Where(…).ToList()` twice in that function would run two identical queries — and, if a row changed in
between, give you two different answers to the same question.

### `ToList` or `ToArray` — which materialiser   {#tolist-or-toarray}
Both run the query and bring back the same rows. They differ only in the type you are left holding, and that is the
same difference C# draws:

- **`.ToList()` answers a `List<Entity>`** — you can `.Add` to it, so it is what you want when the rows are the start
  of something you are still assembling.
- **`.ToArray()` answers an `Entity[]`** — fixed-size, and the plainer statement when the rows are the answer.

A `List<T>` is accepted anywhere a `T[]` is asked for, because giving up `.Add` is always safe. The reverse is not: a
`T[]` is not a `List<T>`, and a function that wants one says so by materialising with `.ToList()`.

```osy title="the two materialisers, and the one-way conversion" test app=query-tolist
List<Order> Growing(decimal min) { return Order.Where(o => o.Total >= min).ToList(); }
Order[]     Fixed(decimal min)   { return Order.Where(o => o.Total >= min).ToArray(); }

// A List goes where an array is wanted — no conversion written, none needed.
Order[] Widened(decimal min) { return Order.Where(o => o.Total >= min).ToList(); }

// …and `.ToArray()` says it out loud, on a list you built yourself.
string[] Codes(decimal min) {
  var codes = new List<string>();
  foreach (var o in Order.Where(o => o.Total >= min).ToList()) { codes.Add(o.Code); }
  return codes.ToArray();
}
```

⚠ **`.ToArray()` takes a COPY.** Adding to the list afterwards does not change the array you already took — that is
what makes "fixed-size" mean anything.

### When you only want a number   {#count-instead}
If all you need is how many, do not materialise the rows to count them. `Count()` asks the database for the number and
brings back one integer instead of ten thousand rows. See [Where / Single / Count](https://osysharp.com/reference/query/where/).

## See also       {#see-also}
- [Where / Single / Count](https://osysharp.com/reference/query/where/) — building the query `ToList` runs
- [Skip / Take (paging)](https://osysharp.com/reference/query/paging/) — `Skip` / `Take`, to materialise one page instead of everything
- [foreach](https://osysharp.com/reference/function/foreach/) — walking what came back
