Skip to main content

2026-09: One kind of command

The Entity command, Entity batch command, Child entity command and Child entity batch command types are gone. Every command is now a command with a model, and the model says what the command works on. Existing applications were migrated automatically; this page explains what the migration did and what changed for a command that runs from a Create page.


What is changing​

Command types. A command's model now declares the entity it works on: a Direct reference for one record (model.Entity), an Entity Collection reference for a selection (model.Selection, an ICollection backed by a query), a second Direct reference for a parent. The four entity command types were shorthand for those shapes; the shorthand is gone, the shapes stay.

Parameter mapping everywhere. Each place that invokes a command - page action, action button, Data grid command, business event - now carries an explicit mapping into the command's model, instead of the platform guessing from the command's type what to inject. The expression signatures are listed in Commands - Parameter mapping.

Page action types. Execute entity command and Execute custom command became Execute command; Link custom command page became Open command window. Data grid commands carry the same two names as their Execution mode.

Page actions have two new behaviour settings. Before executing (validate and save the page, validate only, execute without validating) and After the command (dismiss the page, or stay on it). A new action gets the defaults, Validate and save the page and Dismiss the page; the migration set existing ones to what they did before (see below). Data grid commands got Before executing too, defaulting to Execute without validating, which is what they did before. See Entity Pages - Actions.

Data grid expressions take the row and the page. Column expressions and highlight conditions now take (row, model, db, ctx) - the same arguments as a grid command's conditions - so a column can read the page it sits on. A column expression used to take the row alone and a highlight condition (row, db, ctx). The Document display text expression takes the document, (document) => ..., and the Summary display expression the computed value, (value) => ...; both are unchanged. See Data Grid - Column expressions.

Commands on a Create page. The immediate command adapter is gone. A command run from a Create page receives the page's entity as a draft - a detached copy under the form's id. What the body changes on it comes back into the form; a save inside the command does not write the draft; the body may insert it itself with db.<Set>.Add(model.Entity). See Commands on a Create page.


What the migration did​

For every application the platform:

  1. converted each entity command into a command with a model: the entity became a Direct reference (a selection an Entity Collection reference, a parent a second Direct reference). The command body is kept as it was; only the lambda's head changed, and the parameters the platform used to inject became local variables of the same names, read from model.Entity, model.Parent or model.Selection.Query() - so a batch command's body keeps the IQueryable it was written against. The Execution condition was converted the same way; it now runs after the model is filled, so it sees the mapped values. Converted commands open in a Medium modal;
  2. wrote the parameter mapping at every place the command is invoked - (model, db, ctx) => model on page actions and action buttons, (row, model, db, ctx) => row on grid row commands, (selection, model, db, ctx) => selection on grid batch commands and (changes, db, ctx) => changes.Entity on business events. A former child command got its parent mapped from the page (model) or the event (changes.Entity), and on a business event its child from the changesets its child triggers fire on, e.g. (changes, db, ctx) => changes.Items.Added.Concat(changes.Items.Modified).FirstOrDefault() for an event triggered by an added or an updated child;
  3. added model - the page's entity - as the second argument of the grid command conditions: (row, db, ctx) => ... became (row, model, db, ctx) => ... for a row command's Enabled and Visible condition and for a batch command's Enabled condition. A batch command's Visible condition already took the page's entity and was left as it was, (model, db, ctx) => ...;
  4. set Execution mode to Execute command on every grid command, and renamed the page action types;
  5. set After the command to Stay on the page on the command actions of Create pages, so a command that fills the form keeps the form open as before. Every other action keeps Dismiss the page, which is what it did already. A command action inside a shared view used on a Create page was left alone and reported, because the same view may sit on a Detail page; set it by hand if the view is only used on Create pages;
  6. set Before executing on every command action to what it did before: Validate only for a former entity command, Execute without validating for one on a Create page (so that it can still fill an incomplete form) and for every custom command. A command action in a shared view used on a Create page and on another page got Validate only and was reported: set it by hand if it has to fill an incomplete Create form;
  7. added model, db and ctx after the row to every column expression of every grid - item => item.Name became (item, model, db, ctx) => item.Name - and model to every highlight condition. Every expression slot of a column was converted, including those of a type the column no longer has, so switching a column's type back does not bring an old expression back. A parameter that already had one of the new names was renamed with a leading underscore throughout the expression (model became _model) and reported. Comment lines above an expression were kept.

A grid row command that was also used as a batch command got a <Name>_Batch twin that receives the selection and runs the original body per row.


What to check in your application​

  • A create-page command that saved the record itself (db.<Set>.Add(...) followed by a redirect to the detail) keeps working: the draft is added under the form's id.
  • A create-page command that only filled fields keeps working; the modal stays open because the migration set Stay on the page. If such an action lives in a shared view, set After the command yourself.
  • A command that read the entity from the database by id on a Create page never found it before either; it now has the draft in model.Entity instead.