# Update

> Ends a query chain with a set-based UPDATE: every row the chain selects gets the assignments applied, in the database, immediately — and the call answers how many rows were written. A value may read the row itself (`o.Balance - o.Fee`), so per-row arithmetic runs as SQL and increments compose under concurrency.

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

## Summary        {#summary}
`.Update(…)` ends a query chain the way [`.Delete()`](https://osysharp.com/reference/query/delete/) does — but instead of deleting the matching
rows it **writes** them: the body is a list of assignments to the row, applied set-based in one statement, at the
call site. The answer is how many rows were written. It is the same verb as C#'s `ExecuteUpdate`, under the natural
name and with the natural body — assignments, not a `SetProperty` chain.

## Signature      {#signature}
```osy syntax
<Entity>.Where(o => …).Update(o => { o.Status = "Closed"; })            →   int
<Entity>.Where(o => …).Update(o => { o.Total = o.Total + o.Fee; })      →   int   // the RHS reads the row
<Entity>.Where(o => …).OrderBy(k).Take(n).Update(o => { … })            →   int   // bounded — the chunked pass
```

## Description    {#description}

### Which rows does it write?   {#the-target-set}
Exactly the rows the chain would have returned, narrowed further by the entity's `allow update` rules — **per
assigned property**: a row is updatable only if every property you assign is granted for it, and a caller with no
grant at all for one of the assigned properties is refused naming it. Rows outside the set are untouched, never an
error; the count says how many were written.

```osy title="close every stale order" test app=query-update
entity Order {
  [Required] string Status;
  decimal Total;
  decimal Fee;
}

int CloseStale() {
  return Order.Where(o => o.Status == "Stale").Update(o => { o.Status = "Closed"; });
}
```

### A value can read the row   {#row-referencing}
An assignment's value may reference the row's own properties. It becomes part of the SQL, so each row computes with
**its own** values, and concurrent increments compose instead of losing writes:

```osy title="add each order's own fee to its total — one statement, per-row arithmetic" test app=query-update
int ApplyFees() {
  return Order.Where(o => o.Status == "Closed").Update(o => { o.Total = o.Total + o.Fee; });
}
```

A value may also be a captured local or parameter — it binds like a query predicate's would. It may read
**through** the row's references, and it may embed a **correlated scalar read** — an aggregate over the row's own
collection, or an entity-rooted one:

```osy title="hops and correlated aggregates as values — still one statement" test app=query-update
entity Region { [Required] string Name; decimal TaxRate; }
entity Account {
  [Required] string Status;
  Region? Region;
  decimal TaxRate;
  decimal Total;
  [ForeignKey(Account)] Entry[] Entries;
}
entity Entry { [Required] Account Account; decimal Amount; }

int Restamp() =>
  Account.Where(a => a.Status == "Open").Update(a => {
    a.TaxRate = a.Region.TaxRate ?? 0m;             // a hop — null when the reference is absent, so answer for it
    a.Total   = a.Entries.Sum(e => e.Amount);       // ITS OWN entries, correlated per row
  });
```

A hop through a reference that can be absent is null for rows without one — assigning that to a non-nullable
member refuses at compile until you answer for absence (`?? <fallback>`) or declare the member nullable, the same
standard an empty-set `Max(…)` holds. What a value may NOT be is a **row-returning** query: a set statement
assigns one scalar per row — aggregate it, or compute it into a local first.

### When does it run?   {#immediacy}
Immediately — at the call, not at `UnitOfWork.Commit()`, exactly like [`.Delete()`](https://osysharp.com/reference/query/delete/). It writes the
stored rows, so it refuses (naming the remedy) while your unit of work holds uncommitted changes of the same type.

### What about the entity's rules?   {#constraints}
They hold. An `[Immutable]` property, a workflow-owned state field or a platform-stamped field is refused **at
compile time**, naming the reason. Value rules (`[Min]`, `[Max]`, `[Pattern]`, `[MinLength]`, `[Required]`) and the
entity's `invariant`s are re-checked over the written rows **inside the same transaction** — one violating row
rolls the whole statement back, with the rule's own message:

```osy title="a guarded increment — one row over the limit rolls everything back" test app=query-update
entity Meter {
  [Required] string Zone;
  [Max(100)] int Load;
}

int Shed(int by) {
  return Meter.Where(m => m.Zone == "North").Update(m => { m.Load = m.Load + by; });
}
```

### A list in memory?   {#local-lists}
`.Update(…)` writes **database rows**. Elements of a local list change with a plain loop —
`foreach (var x in xs) { x.Prop = value; }` — and the compiler says so if you reach for the wrong verb.

## Examples       {#examples}
```osy title="a maintenance pass with a bound batch" test app=query-update
int ArchiveOldest() {
  return Order.Where(o => o.Status == "Closed").OrderBy(o => o.Total).Take(100)
              .Update(o => { o.Status = "Archived"; });
}
```

## See also       {#see-also}
- [Delete](https://osysharp.com/reference/query/delete/) — the delete terminal, same shape and same security story
- [Where / Single / Count](https://osysharp.com/reference/query/where/) — saying which rows
- [security { }](https://osysharp.com/reference/security/entity-security/) — the `allow update` rules that narrow the set, per assigned property
