# 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 rules — nothing is inherited here
}
```

## 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 it tracks 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 `[Unique]` reaches further than you might expect — see
[Unique spans the hierarchy](#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 |
| `(Contract)d` | a written downcast must fail at run time when the row is not one, and that check is not built |

### Security is never inherited          {#security}
A subtype declares its **own** `security { }` block, and inherits nothing from its base.

That is deliberate, and it is 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. A row is always governed by the rules of **its actual type**, never by the type you reached
it through.

```osy title="the subtype states its own rules" test app=entity-inheritance-security
[Role] enum AppRole { Authenticator, Member }

[Principal]
entity User {
  [Required, MaxLength(255)] string Email;
  security { allow read when IsAuthenticated; }
}

entity Document {
  [Required, MaxLength(200)] string Title;
  security { allow create, read when IsAuthenticated; }   // any signed-in user sees any Document
}

entity Contract : Document {
  [Required, MaxLength(80)] string Counterparty;
  // Its OWN, 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.
  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 { }` | **no** — always its own | it is an *authority* question, where silence must mean no |

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.

**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]` spans the hierarchy       {#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. There
is no per-type spelling: if hierarchy-wide is not what you meant, the member belongs on one type rather than on a
shared one. The same warning appears for a composite `[Unique(A, B)]` and for a `[Unique]` a subtype declares.

### 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) |

### Deriving from a platform type        {#platform-types}
A type a capability brings in may be derived from when it is not `sealed` — today that is `AgentTask`, so an app can
give its own kind of agent work its own type, its own rules and its own process.

**A subtype of a platform type 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 already follows. Your own types are unrestricted: a subtype of a type *you* declare adds whatever it likes.

```osy title="an app's own kind of task, with its own loop" test app=entity-inheritance-platform
using Osysharp.Agents;

[Principal] entity Person {
  [Required, MaxLength(255)] string Email;
  security { allow read when IsAuthenticated; }
}

// A distinct type sharing AgentTask's table — no new columns anywhere.
entity PersonalTask : AgentTask {
  security { allow read when IsAuthenticated; }
}

workflow PersonalTaskProcessor {
  Tracks  = PersonalTask.Status;
  Initial = Running;
  state Running { }
  state Waiting { }
  terminal success Completed { }
  terminal error   Failed { }
}

app.Agent = new AgentConfig { Loop = PersonalTaskProcessor };
```

The task rows an agent opens under that loop **are** `PersonalTask`s: a task's type is the type its loop tracks. A
plain agent with no loop, or one whose loop tracks `AgentTask` itself, still gets an `AgentTask`.

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.

Its fields, references and child collections all come through: `personalTask.Children` reads the same relation
`AgentTask.Children` does, because it IS that relation — there is one table, one foreign key, and one relation over
it, whichever type you reach them through.

### Workflows bind one type              {#workflows}
`Tracks = RushOrder.Status` binds `RushOrder` and nothing else — a workflow over a base does **not** cover its
subtypes. That is what keeps *one state machine per column* true: a base's run and a subtype's run would otherwise
both drive one `Status` value on one row.

It is also what makes inheritance the natural way to give two kinds of thing two different processes: give each its
own type, and each type its own workflow.

## 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)
