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

Reference / UI

reading data inside an action

action Look() { var rows = Order.Where(o => o.Code == code); } — the read runs when the action runs

A data read written inside an `action`, `method` or lifecycle hook runs at that point in the body: the platform fetches it from the server and the body continues with the rows. It is not a page-load value and has no `var` / `live var` decision to make, because a body runs once — when the user triggers it.

stable3 examples compiled by CIuidataactionsauthoring

Summary#

Write the read where you need the answer.

action Refresh() {
  var rows = Order.Where(o => o.Code == code);
  found = rows.Count();
}

The read happens on the server, when the click happens. The body waits for it and carries on — the same way it waits for a server function it calls.

Signature#

action <Name>(…) {
  var <rows>  = <Entity>.Where(…).OrderBy(…);          // the rows
  var <one>   = <Entity>.SingleOrDefault(…);           // one row, or null
  var <n>     = <Entity>.Count(…);                     // how many
  var <any>   = <Entity>.Any(…);                       // whether any
  var <total> = <Entity>.Sum(x => x.<Column>);         // a computed value
}

The same is true in a method, an on mount, an on change and every other imperative member. A render expression is a different question — see component.

Description#

Why this is not the same as a page-load query#

A component member is a binding: it is re-evaluated as the page renders, so declaring one makes you choose what should happen when its inputs change — var fetches once and can go stale, live var re-reads and pays a round trip each time. That choice is real, and it is why a read cannot simply be hoisted for you.

A body is not a binding. It runs once, at the moment the user triggers it. There is no "when should this happen again?", nothing can go stale, and there is no decision for anyone to take. So the read simply runs, there, then.

That distinction is the whole feature, and it decides where to put a read:

you wantwrite it
something the page SHOWSa member — var for a snapshot, live var to follow changes
something an action NEEDS in order to actthe read, in the action

Putting an action's read on a member is not merely more code. The member is fetched with the page — before the user did the thing that made them want the answer — so the action would act on a picture from earlier.

What it costs#

A round trip, at that point in the body. That is what you asked for by writing it there, and an action is already a multi-request thing. Two reads in a body are two round trips, in order; if you need them together, ask once.

Only VALUES cross to the read#

The read runs on the server. Values you hold travel to it — a local, a component member, a route parameter, the id of a row you are showing:

action Look(Customer c) {
  var theirs = Order.Where(o => o.Customer.Id == c.Id);   // ✓ a Guid crosses
}

A whole row does not travel. Comparing against one is refused, and the compiler names the row and shows you the id form:

action Look(Customer c) {
  var theirs = Order.Where(o => o.Customer == c);         // ✗ refused: `c` is a row
}

This is the same rule everywhere in the language, said once: a row on the client is its identity, and its fields live in the store. Nothing about it is special to a body.

What comes back#

Rows come back as rows: read their fields, loop them, filter them further in memory.

action Look() {
  var rows = Order.Where(o => o.Total > 100);
  var big  = rows.Where(o => o.Priority);     // in memory, no second round trip
  foreach (var o in rows) { Log.Information("{Code}", o.Code); }
}

They also land in the page's own store, so a row you then edit is the row the page is already showing — not a detached copy of it.

Security is not a question here#

The read goes out under your own principal and comes back through the same secured read every other query uses. A body cannot ask for more than the page could, and there is nothing to check or arrange: see security { }.

Examples#

Look something up on a click and show the answer:

entity Note {
  [Required, MaxLength(80)] string Title;
  int Rank;
  security { allow read when IsAnonymous || IsAuthenticated; }
}

[Page("/look")]
[Render(CSR)]
[AllowAnonymous]
component LookPage() {
  int total = -1;

  action Look() {
    var rows = Note.Where(n => n.Rank > 1);
    total = rows.Count();
  }

  render {
    Stack(gap: 2) {
      Text("total:" + total);
      Button("Look", onPress: Look);
    }
  }
}

Key the read off something the page holds — the value is read at the click, not at page load:

[Page("/search")]
[Render(CSR)]
[AllowAnonymous]
component SearchPage() {
  string wanted = "";
  string found = "-";

  action Search() {
    var one = Note.SingleOrDefault(n => n.Title == wanted);
    found = one == null ? "none" : one.Title;
  }

  render {
    Stack(gap: 2) {
      Input(value: wanted, placeholder: "Title");
      Text("found:" + found);
      Button("Search", onPress: Search);
    }
  }
}

Ask a question rather than fetching rows to count them:

[Page("/tally")]
[Render(CSR)]
[AllowAnonymous]
component TallyPage() {
  int total = 0;
  string state = "-";

  action Tally() {
    total = Note.Sum(n => n.Rank);
    state = Note.Any(n => n.Rank > 2) ? "has-high" : "all-low";
  }

  render {
    Stack(gap: 2) {
      Text("total:" + total);
      Text("state:" + state);
      Button("Tally", onPress: Tally);
    }
  }
}

See also#

  • component — declaring a component, and its var / live var members: the read a page SHOWS, and the choice that comes with it
  • Session.CurrentUser — the one read that is fetched with the page, because it has no inputs that can change
  • creating & saving data — writing in an action, and when the write is committed
  • security { } — who may read what, declared once on the entity

Related

component

The one archetype for all UI: a bounded reactive unit — typed props, reactive members (fields, `live`…

Session.CurrentUser

`Session.CurrentUser` is the person the app is being shown to, as your own `[Principal]` entity. Read it in a member…

creating & saving data

A UI `action` creates, updates and deletes data by writing `new Entity { … }`, assigning fields, and calling…

security { }

The rules that decide who may read and write an entity's rows. A where clause filters by the row (the owner sees their…