The ARX specification

02 The schema catalog

The **schema catalog** is what the developer declares about the tables, once, at startup, and gives to the service. A service with a catalog never reads table definitions from the database during an operation; only an explicit startup step does (the preload, below). A service created without a catalog, on a provider that declares the SchemaResolution capability (08), reads each table's id column, keys and column types before the call that needs them; that is the one exception, and the developer chooses it by leaving the catalog out.

Ids

Every table declares one id column or none:

Declaration Meaning
Generated, of a type (ArxIdColumn.Generated<long>("id")) The database (or the provider) makes the id; Create returns it. A provider whose database can't generate that type must refuse with ARX8001 (DynamoDB: no generated ids; MongoDB: no generated integers; MySQL: no generated UUIDs)
Supplied, of a type (ArxIdColumn.Supplied<string>("code")) The application gives the id in the entity's attributes
No id column (ArxIdColumn.NoIdColumn; NoPrimaryKey before 0.1.0-preview.4) The table has no id; Create returns "none". A provider whose records always have one must refuse (MongoDB: ARX1001)
  • There is no default: an entity whose table declares no id, in a service without a declaration for it, is ARX1016 IdNotDeclared.
  • Without a catalog, an entity may carry its own declaration (entity.IdColumn, entity.Keys) for that one call; with a catalog, doing so is ARX1001, so a table is declared in one place.
  • Create reads the id to write only from the attributes. An id read back as a different type than declared is ARX5003 SchemaMismatch, never a raw driver error.

A record id (ArxRecordId) is the id column's name and its value, or "none". It is what Create returns and what entity.RecordId holds.

Business keys

A table may declare keys: sets of one or more columns whose values identify one record (.Key("price_list", "region", "sku")). Update, Delete and Retrieve may find records by a declared key (ArxMatch.Key), by the id (ArxMatch.Id, the default) or by any columns the developer names (ArxMatch.Columns).

Version columns

A table may declare one version column (.Version("product", "row_version")): an integer, not the id or part of a key. A provider that declares OptimisticConcurrency keeps it: Create writes 1 when the entity gives none; an Update or Delete whose entity carries the version it read is conditioned on the record still having it, and an Update adds one (without a version the update isn't conditioned, but the version still goes up). A record that is there at another version is ARX2004 RecordChanged, and nothing is written; a record that isn't there stays ARX2002. A provider without the capability refuses a schema that declares a version column with ARX8001, wherever it takes one.

Name aliases

A table may declare aliases for columns whose names fail the identifier rules (chapter 07): .Alias("customer", "first_name", "first-name"). The attribute must be a valid name; the database name may hold any character but a control character. Code uses the attribute; the provider sends the database name, quoted and escaped by its database's rules (or as the field or attribute name, where names are data), and reads the column back under the attribute: in writes, reads (every column included), conditions, order, matches, and the id and keys, which are declared by their attributes. Only declared names pass: a call that uses the database name itself is refused as any invalid name, so a database name never comes from a call. A link entity's columns keep the identifier rule. A provider may refuse an alias its database can't keep: one MongoDB would read as an operator or a path ($ first, a .) is ARX1003; one on an attribute a DynamoDB model names (a key, an index key) is ARX8001.

Lookups and display names

A lookup declares that a column holds the id of a record of another table (.Lookup("contact", "account_id", "account")). A reference value written there is checked against the catalog first (the referenced table is declared, has an id, of the right type: ARX1005 InvalidLookup); the database's foreign key, where it has one, checks the record exists (ARX3002). Read back, a declared lookup is a reference with the referenced record's display name (.DisplayName("account", "name")).

The preload

A provider may offer one explicit startup step that reads the ids, keys, lookups and, optionally, column types of the tables the developer lists, and returns a catalog (ArxPreLoadTable … LoadAsync). It runs once; operations never read the catalog again. In Strict mode the preloaded columns are an allow list: any other column in a write, a condition or a column set is ARX1018 NotPreloaded, before the database.

All chapters of the specification