# entity Sub : Base

> Derives one entity from another. The subtype is its own type with its own name, its own security and its own workflows, and it carries every member of its base. Both store their rows in one table.

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

## Summary        {#summary}
`entity RushOrder : Order` derives one entity from another, with C#'s colon and C#'s meaning. The subtype is its
**own type** — its own name, its own security rules, its own workflows — and it carries **every member of its base**
plus whatever it adds. Both types store their rows in **one table**.

Those two facts are the whole feature, and everything below is a consequence of one or the other.

## Signature      {#signature}
```osy syntax
entity <Sub> : <Base> {
  <attributes> <Type> <Member>;    // members of its OWN, on top of everything Base declares
  invariant <condition>;           // its own checks; Base's still apply
  security { … }                   // its OWN grants — only Base's DENIES carry over
}
```

## Description    {#description}

### What a subtype is                    {#what}
A subtype is a **distinct type**. It has its own name, and everything the platform keys on a type keys on it
separately: its security rules are its own, a workflow that tracks a column of its OWN drives only it, and a row of it
reads back as itself.

It is also **complete**. You do not reach through a base to get at inherited members — `RushOrder` simply *has*
`Reference`, in a query, in a UI binding, in a function:

```osy title="a base, a subtype, and both of their members" test app=entity-inheritance
entity Order {
  [Required, MaxLength(120)] string Reference;
  int Quantity;
}

entity RushOrder : Order {
  [Required, MaxLength(60)] string Courier;
}

void Ship() {
  // `Reference` and `Quantity` come from Order; `Courier` is RushOrder's own. No difference at the use site.
  var rush = new RushOrder { Reference = "R-1001", Quantity = 2, Courier = "DHL" };
}
```

### Where are a subtype's rows stored?        {#storage}
A subtype's rows live in its **base's table**, alongside the base's own. A hidden platform-written column records
which type each row is.

This is not an implementation detail you can ignore, because it is what makes the useful things possible: a
reference typed `Order` can hold a `RushOrder`, a parent-child tree can span types, and there is **one id space**, so
nothing has to ask "which table is this id in". It is also why a subtype may not redeclare a base member — see
[One column, so no redeclaring](#one-column) — and why a `[Unique]` on a base reaches further than you might expect,
while one on a derived type does not — see [Unique on a base, unique per type](#unique).

One consequence is worth knowing if you ever look at the table directly: **a member only a subtype declares is
optional in the database**, because rows of every other type genuinely have no value for it. `[Required]` is still
enforced — for that type, on every write — but the column itself holds NULL for everyone else, rather than a
fabricated blank.

### A subtype goes wherever its base is wanted   {#upcast}
A `SignedContract` **is** a `Document`, so it goes anywhere a `Document` is expected — a member, a function
argument — with no cast, exactly as in C#. This is what one table and one id space buy: a single reference column
holds any kind, and nothing at the far end knows the hierarchy exists.

```osy title="one FK, every kind of document" test app=entity-inheritance-upcast
entity Document { [Required, MaxLength(200)] string Title; }
entity Contract : Document { [Required, MaxLength(80)] string Counterparty; }

entity Note {
  [Required] Document Document;                 // typed as the ROOT…
  [Required, MaxLength(400)] string Body;
}

void Annotate() {
  var c = new Contract { Title = "Supply", Counterparty = "Acme" };
  new Note { Document = c, Body = "countersigned" };   // …and a Contract goes straight in
}
```

"Anywhere a `Document` is expected" includes the places where two values have to agree on one type — a `?:`, a
`switch` expression, a `??` fallback. The result is the **base** of the two, so `Document d = rush ? contract : doc;`
is the ordinary way to pick between them. Two SIBLINGS — a `Contract` and an `Invoice` — have no common type to
*infer*, so they take the type they are written INTO: `Document d = rush ? contract : invoice;` is fine, while
`var d = …` has nothing to take and says so.

The other direction — treating a `Document` you are holding as a `Contract` — can fail at run time, so you state it:
[test the row](#narrowing) with `is`, or narrow a whole set with `OfType<T>()`.

### Reading a type returns everything below it   {#reads}
`Order.Where(…)` returns rush orders too, because a `RushOrder` **is** an `Order` — the same thing a `List<Order>`
means in C#. **Narrowing is what you state:** `RushOrder.Where(…)` returns rush orders and whatever derives from
them, and never a plain `Order`. `Count()`, a collection you navigate to and a tree you walk all read the same way.

Each row that comes back is governed by [its own type's rules](#security), never by the type you asked through — so
a base read can return fewer rows than the table holds, and that is the rules working rather than a missing row.

```osy title="a base read returns every kind below it" test app=entity-inheritance
int AllOrders() {
  // The rush orders too — they are orders.
  return Order.Where(o => o.Quantity > 0).ToList().Count;
}

int RushOnly() {
  return RushOrder.Where(o => o.Quantity > 0).ToList().Count;
}
```

### How do I get back just one kind? — `is` and `OfType<T>`      {#narrowing}

A base read hands you every kind. **`is` tests one row; `OfType<T>()` narrows a whole set** — and both are
polymorphic downward, because a `SignedContract` **is** a `Contract`.

`x is Contract` is a plain condition: it works in a query, where it becomes a check the database does without
reading any rows, and in ordinary code. `is not Contract` is its negation.

```osy title="test one row, narrow one set" test app=entity-inheritance-narrow
entity Document { [Required, MaxLength(200)] string Title; }
entity Contract : Document { [Required, MaxLength(80)] string Counterparty; }
entity SignedContract : Contract { [Required, MaxLength(60)] string Signatory; }

// In a query — the database answers it; nothing is loaded to decide.
int ContractCount() { return Document.Where(d => d is Contract).ToList().Count; }

// `OfType<T>()` gives you a set of that type, so its own members are readable.
string Counterparties() {
  var all = "";
  foreach (var c in Document.OfType<Contract>().ToList()) { all = all + c.Counterparty + ";"; }
  return all;
}
```

Both count the `SignedContract` too. To ask for *only* the leaf, name it: `Document.OfType<SignedContract>()`.

#### Why can't I read the subtype's members after `is`?               {#narrowing-pattern}

`is` on its own answers a question; it does not change what you may read. `d is Contract` tells you the row is a
contract, and `d` is still a `Document`, so `d.Counterparty` does not compile. Give the test a **name** and it does:

```osy title="one list, one loop, rendered per kind" test app=entity-inheritance-narrow2
entity Document { [Required, MaxLength(200)] string Title; }
entity Contract : Document { [Required, MaxLength(80)] string Counterparty; }
entity Memo : Document { }

string Render() {
  var lines = "";
  foreach (var d in Document.OrderBy(x => x.Title).ToList()) {
    if (d is Contract c) {
      lines = lines + d.Title + " with " + c.Counterparty + "\n";   // `c` is the same row, as a Contract
    } else {
      lines = lines + d.Title + "\n";
    }
  }
  return lines;
}
```

`c` is the row you tested — nothing is copied — and it exists **inside the `if` only**. That is deliberate: outside
the branch the test may not have held, and a name that reads a row as a kind it is not would be worse than no name.

For the same reason these are refused, each with a sentence saying what to write instead:

| Written | Why |
|---|---|
| `is not Contract c` | the test *failing* says nothing about what `c` would be |
| `var b = d is Contract c;` | `c` belongs to a branch, and there is no branch here — use `is Contract` to get the answer |

A **cast** is the other way to say it, for a row you know is one: `(Contract)d` is the same row as a `Contract`, and
when it is not one it fails — `InvalidCastException`, naming both types — rather than reading on as the wrong kind.
`d as Contract` answers null instead. Casting UP (`(Document)contract`) always succeeds. Inside a query, where the
database answers, a cast that could fail is refused: test with `is`, or narrow the read with `OfType<T>()`.

```osy title="a cast for a row you know the kind of, and what a wrong one says" test app=entity-inheritance-cast
entity Document { [Required, MaxLength(200)] string Title; }
entity Contract : Document { [Required, MaxLength(80)] string Counterparty; }

string CounterpartyOf(Guid id) {
  var c = (Contract)Document.Single(d => d.Id == id);   // the same row, as a Contract
  return c.Counterparty;
}

string WhatAWrongCastSays(Guid id) {
  try { return ((Contract)Document.Single(d => d.Id == id)).Counterparty; }
  catch (InvalidCastException e) { return e.Message; }
}
```

### Grants are never inherited — denies are          {#security}
A subtype declares its **own** `security { }` block: none of its base's **grants** carry over. Every **deny** its base
states does.

Both halves are the safe direction. An entity with no policy grants nothing, so a subtype whose author has not yet
thought about who may read it returns **no rows** — rather than silently receiving whatever grant its base happened to
have. And a subtype's row IS a row of its base — one table, and a base read returns it — so a deny that stopped at the
base's own rows would narrow nothing: `deny read where Withdrawn` on `Document` would leave every withdrawn `Contract`
readable. A deny only ever narrows, so carrying it down cannot open anything. The subtype's own rules cannot widen past
it either — an inherited deny is decided after every rule the subtype states.

The same holds for a deny you state on a type you did not write: `partial entity User { security { deny read … } }`
over a kit's `User` holds on every type derived from it, the kit's own and yours.

A subtype may also **narrow a field it inherits** with a deny of its own — `deny read Notes when !IsReviewer;` on a
`Memo : Document` — and never widen one: an `allow` that names an inherited field is refused
([deny on an inherited field](https://osysharp.com/reference/security/inherited-field-deny/)).

```osy title="the subtype states its own rules" test app=entity-inheritance-security
[Principal]
entity User {
  [Required, MaxLength(255)] string Email;
  security { allow read when IsAuthenticated; }
}

entity Document {
  [Required, MaxLength(200)] string Title;
  bool Withdrawn;
  security {
    allow create, read when IsAuthenticated;   // any signed-in user sees any Document…
    deny read where Withdrawn;                 // …that has not been withdrawn
  }
}

entity Contract : Document {
  [Required, MaxLength(80)] string Counterparty;
  [CreatedBy] Guid? CreatedBy;
  // Its OWN grant, and narrower. Nothing of Document's grant carries over — had this block been left out entirely, a
  // Contract would be readable by nobody, which is the direction you want to be wrong in. Document's DENY does carry
  // over: a withdrawn Contract is hidden from everybody, its creator included.
  security { allow create, read where CreatedBy == user.Id; }
}
```

### What IS inherited                    {#inherited}

| Declaration | Inherited? | Why |
|---|---|---|
| members, and their attributes (`[Required]`, `[MaxLength]`, `[Searchable]`, …) | **yes** | they travel with the member, which the subtype has |
| `semantic => …` (the search card) | **yes**, and a subtype may declare its own to override | it is a *description*, and a subtype that adds nothing genuinely describes itself the same way |
| depth | **unlimited** — `SignedContract : Contract : Document` carries every member of both levels above it | |
| `invariant` | **yes**, and the subtype's own add to them | it is a *constraint*, and constraints only accumulate — a subtype cannot drop one |
| `security { }` grants | **no** — always its own | it is an *authority* question, where silence must mean no |
| `security { }` denies | **yes**, decided after the subtype's own rules | a deny only narrows, and the subtype's rows are the base's rows |
| a field rule on an inherited field | **a `deny` only** — an `allow` is refused | the base is the floor; below it permissions only narrow |

Description defaults to inheritance; authority defaults to denial.

### Can I redeclare a base's member on a subtype?        {#one-column}
Because both types share a table, a member declared on both is not a shadowed member — it is **one column claimed by
two declarations**. C# lets you shadow with `new`; a table cannot, so there is no spelling for it and the compiler
asks you to delete one:

```osy title="✗ a subtype redeclaring a base member — one column, two claims" syntax
entity Order    { [Required] string Reference; }
entity RushOrder : Order {
  [Required] string Reference;   // ✗ 'RushOrder.Reference' redeclares 'Order.Reference'
}
```

The realistic way to hit this is not a typo — it is a base gaining a member later that a subtype already used. The
error names both declarations so you can see which one you meant.

**To type an inherited REFERENCE narrower on the subtype**, which is a different intent, say so with
[`override`](https://osysharp.com/reference/entity/override/): `override Company Organization;` is the base's one column, typed as a type derived
from the base's, and the redeclaration error above suggests it whenever the member is a reference.

**Two SIBLINGS may share a member name, as long as they agree about it.** `Rush.Note` and `Standby.Note` do not
collide with each other the way a subtype collides with its base — they are different rows — so one shared column
serves both:

```osy title="siblings sharing a name, agreeing about it" test app=entity-inheritance-siblings
entity Ticket { [Required, MaxLength(200)] string Title; }

entity Bug      : Ticket { [MaxLength(80)] string Area; }
entity Feature  : Ticket { [MaxLength(80)] string Area; }   // same column, same meaning — fine
```

What is refused is the two **disagreeing**, because there is only one column and only one of them can win:

```osy title="✗ siblings disagreeing about the shared column" syntax
entity Bug     : Ticket { [MaxLength(80)] string Area; }
entity Feature : Ticket { int Area; }    // ✗ one column, declared as `string` by one type and `int` by the other
```

### `[Unique]` on a base, and unique per type       {#unique}
A unique constraint is over a **table**, and one table holds every type in the hierarchy. So `[Unique]` on a base is
unique across the base *and every type derived from it*:

```osy title="one code, across every kind of order" test app=entity-inheritance-unique
entity Order {
  [Required, Unique, MaxLength(40)] string Code;   // no two Orders share a Code…
}

entity RushOrder : Order {                          // …and a RushOrder is an Order, so it is in the same space
  [Required, MaxLength(60)] string Courier;
}
```

An `Order` with `Code = "A-1"` and a `RushOrder` with `Code = "A-1"` is refused. That is usually exactly what you
want — one id space is much of the point of sharing a table — but it is impossible to read off the declaration, and
the other reading ("unique among Orders") is equally plausible. **So the compiler warns**, naming every type the
constraint covers:

> `'Order.Code' is unique across EVERY type stored in 'Order's table — Order, RushOrder — not just 'Order'.`

It is a warning rather than an error because the behaviour is correct; what was missing is that you were told.

**A `[Unique]` written on a derived type is unique per type.** It holds among that type's own rows, and a sibling type
sharing the table may hold the same value — the database enforces it, whatever writes the row. Write it on each type
that needs it, naming a member the type declares or one it inherits:

```osy title="one name per kind of tag; two kinds may share one" test app=entity-inheritance-unique-per-type
entity Tag {
  [Required, MaxLength(40)] string Name;
}

[Unique(Name, "Another product tag already has that name.")]
entity ProductTag : Tag { }

[Unique(Name, "Another customer tag already has that name.")]
entity CustomerTag : Tag { }
```

A `ProductTag` and a `CustomerTag` may both be called "Wholesale"; a second `ProductTag` called "Wholesale" is refused
with the sentence written beside the rule. "Per type" means exactly that type: a type derived from `ProductTag` has
rows of its own type, and declares its own `[Unique]` if it needs one.

### What happens to the rows if I remove a subtype?                   {#removing}
Deleting a subtype from your source means the same thing as deleting any other entity: **its rows go, and nobody
else's do.** It does not drop the shared table, the base and its siblings are untouched, and the columns that only
the removed type declared go with it.

Like every other drop it is gated — a plain recompile reports it and keeps the type, and removing it for real needs
`--prune` in development or an acknowledged migration in production.

Deleting a **base** while something still derives from it is a compile error: the subtype's `: Base` names a type
your application no longer declares. Remove the subtype in the same change, or keep the base.

### What is refused                      {#refused}

| Written | Refused because |
|---|---|
| a `sealed` base | the type said no type may derive from it — see [sealed](https://osysharp.com/reference/entity/sealed/) |
| two bases (`: A, B`) | Osy# has no interfaces, so a second name could only be a second base, and a row has one type |
| `class X : Y` | a class is an in-memory value, not a table; give it a field of the other type and compose |
| an entity deriving from a class, or the reverse | different kinds — one is a table, one is not |
| `extends` / `implements` | other languages' spellings; Osy# keeps C#'s colon, one spelling per concept |
| two siblings declaring one member DIFFERENTLY | one column, and only one of the two declarations can win |
| a written DOWNCAST (`(Contract)d`) | it must fail at run time when the row is not one, and that check is not built — [narrow instead](#narrowing) with `is` or `OfType<T>()`, which cannot fail |
| a member other than `security { }` on a subtype of a **platform** type | it shares a table the platform owns and writes — see [below](#platform-types) |
| a member on a subtype of a **package** type that the package's type already has | one column, and the package's declaration is already in it — see [below](#package-types) |

### Deriving from a platform type        {#platform-types}
The types the platform's own capabilities bring in — `Osysharp.Workflow`, `Osysharp.Storage` and their neighbours —
are `sealed`: an app does not derive from them. To state your own access to one, declare a `partial entity` over
it ([security { }](https://osysharp.com/reference/security/entity-security/)).

Where a capability does leave a type open, **a subtype of it declares `security { }` and nothing else.** It shares
a table the platform owns and writes, so adding a column to it would reshape platform storage — the same rule a
`partial entity` over a platform type follows. Rows of a platform type stay platform-written through the subtype:
the new name grants no ability to create, change or delete one that the base did not. Your own types are
unrestricted: a subtype of a type *you* declare adds whatever it likes.

### Deriving from a package's type        {#package-types}
A type from a package you `use` — a shop kit's `Product`, a third party's — is different from a platform type in the
one way that matters here: its table is **yours**, written by your app's own compiled code. So a subtype of it may
add fields, exactly like a subtype of a type you declare:

```osy syntax
using Osysharp.Shop;                 // a package the app uses; it ships `entity Product { string Name; … }`

entity Bike : Product {              // one more kind of product, in Product's own table
  [MaxLength(60)] string Tubing;     // a column only bikes use
  security { allow read when IsAnonymous || IsAuthenticated; }
}
```

The "platform type" rule above is about the capabilities the platform itself ships — `Osysharp.Workflow`,
`Osysharp.Storage` and their neighbours.

**A subtype cannot claim a member its base already has** — it is one column, [as for your own types](#one-column).
That is also what happens when a LATER version of the package adds a member your subtype already declared: the
compile after `osy update` refuses it by name, naming both declarations, and you rename yours or adopt the package's.
Nothing is silently merged, and no row changes meaning without you seeing it.

**A package's own rules are the FLOOR your subtype stands on.** Its grants are not inherited — a subtype still
states its own `security { }`, and without one returns nothing — but whatever the package's type DENIES, it denies on
your subtype's rows too: the package's `deny read PasswordHash when !IsAuthenticator` on
[`User`](https://osysharp.com/reference/security/accounts-kit/) holds on your `Employee : User`, whatever `Employee`'s own block grants. A kit's
guarantee cannot be undone by deriving from its type, which is what lets a kit ship a credential at all.

### Who decides who may change the fields I add?   {#own-fields}
**The fields your subtype adds follow the package's rules until your block names them.** A package's own-row rule —
the UserAccounts kit's "a person edits their own account" — covers every column of the row by default, the ones you added
included. Name one of YOUR fields in an `allow update` rule of the subtype's own block, and for that field your rules decide
instead:

```osy title="a lead assigns a mentor; the account's own fields stay the kit's" syntax
entity Member : User {
  Member? Mentor;                          // the app's own field
  [MaxLength(20)] string Badge = "";       // also the app's own — not named below, so it follows the kit's rules
  security {
    allow read when IsAuthenticated || IsAuthenticator;
    allow create when IsAuthenticator;
    allow update when IsAuthenticated || IsAuthenticator where Id == user.Id;
    allow update Mentor when IsLead;       // ← names the app's field: for Mentor, this decides
  }
}
```

- **Only your own fields.** The package's fields — its own and those it inherits — stay governed by the package. Your
  block may add a `deny` on one of them, which only narrows; an `allow` on one is refused
  ([deny on an inherited field](https://osysharp.com/reference/security/inherited-field-deny/)).
- **A field follows the type that declares it.** The same holds when the extender is itself a kit: kit B's subtype of
  kit A's entity decides the fields kit B adds, on its rows and on any app subtype below it, while kit A's fields stay
  kit A's ([[security-inherited-field-deny#kit-on-kit]]).
- **Only `update`, field by field.** Who may read the row, create it or delete it stays the package's. A write that
  touches one of your named fields AND a package field passes only if both are allowed: a lead above may set `Mentor`
  on anybody's account, and a write of `Mentor` with the account's name in it is refused.

### A workflow over a base covers its subtypes   {#workflows}
`Tracks = WorkItem.Handling` drives `Handling` on every row that **is** a `WorkItem` — its subtypes' rows included —
exactly as a function taking a `WorkItem` takes a `Ticket`. An `Autostart` workflow over a base therefore starts a run
for a new `Ticket` row, `WorkHandling.RaiseRespond(ticket)` takes the subtype, and `Workflow.WorkByItem<Ticket>()`
answers with the tickets among the base workflow's items while `Workflow.WorkByItem<WorkItem>()` answers with all of
them. This is what lets a kit own a process over a base the app derives from — the WorkQueues kit's `WorkHandling` over every
app's `Ticket : WorkItem`.

**One machine per column still holds.** A second `Autostart` workflow over the same column anywhere in the hierarchy —
`Tracks = Ticket.Handling` beside the base's `Tracks = WorkItem.Handling` — is refused at compile, naming both, because
every ticket would get two runs driving one value. Give the subtype its process over a column of its own:

```osy title="the base's process and the subtype's own, each on its own column" syntax
entity WorkItem { Handling Handling; }                 // WorkHandling tracks this, for every kind of item
entity Ticket : WorkItem { TicketStage Stage; }       // the ticket's own lifecycle tracks this

workflow WorkHandling  { Tracks = WorkItem.Handling; Autostart = true; Initial = New; /* … */ }
workflow TicketLife    { Tracks = Ticket.Stage;      Autostart = true; Initial = Open; /* … */ }
// workflow TicketHandling { Tracks = Ticket.Handling; Autostart = true; … }   ✗ refused — WorkHandling drives it
```

An item with two runs, like the ticket above, is still **one** row of `Workflow.WorkByItem<Ticket>()`: its governing
clock is the soonest across both runs, and its holder whoever holds a slot of either.

## Examples       {#examples}

```osy title="two kinds of task, two processes" test app=entity-inheritance-tasks
enum TaskState { Open, Doing, Done }

entity WorkItem {
  [Required, MaxLength(200)] string Title;
  TaskState State = TaskState.Open;
}

// Its own type, so its own rules and its own process — and no new columns.
entity UrgentItem : WorkItem {
  [Required] DateTime DueBy;
}
```

## See also       {#see-also}
- [sealed](https://osysharp.com/reference/entity/sealed/) — how a type says no one may derive from it
- [entity](https://osysharp.com/reference/entity/declaration/) — what an `entity` is, and what the platform provides for free
- [security { }](https://osysharp.com/reference/security/entity-security/) — the `security { }` block a subtype must write for itself
- [invariant](https://osysharp.com/reference/entity/invariants/) — the row-level checks a subtype accumulates from its base
- [relations](https://osysharp.com/reference/entity/relations/) — a reference typed as a base holds any of its subtypes
- **`demo/entity-inheritance`** — the runnable demo: four kinds of document in one table, three levels deep, with per-type
  security you can see by signing in as three different people (`osy docs sample` does not ship it; it lives in the
  repo's `demo/` tree)
