The embedded entity type.
The embedded entity type.
ReadonlyClearReadonlyForeignReadonlyLoadReadonlyNameStable identifier for this companion, unique within its owning entity.
This is the wire key: it appears in serialized payloads and is how the receiving tier finds the companion to deserialize into. Treat it as a published contract — renaming it breaks in-flight payloads and any persisted snapshot that captured them.
ReadonlyOwnerThe entity this companion is attached to. Set by BaseEntity.RegisterCompanion.
ReadonlyRelatedWhether this companion holds unsaved changes.
Rolled up into BaseEntity.Dirty, which is what makes a save actually happen when only the
companion changed. Before companions existed, a clean parent with three new children returned
early from Save() and silently persisted nothing.
True when saving would produce work.
True when the getter will return an object.
True when the owner's FK column refuses null.
The live peer, or null when a nullable FK is not yet provisioned.
Resets the companion to its post-save state — clearing pending removals, rebasing dirty tracking, and so on. Called after the graph commits successfully.
Rebinds the provider used by this companion and its child entities. Subclasses with child entities override this to propagate the provider.
Marks the relationship for removal on the next save. Does not persist by itself.
Contributes this companion's work to the owner's delete plan.
Called before the owner's own node, because children must generally be removed before the parent row they point at. Implementations that rely on database-level cascade delete should contribute nothing.
The plan being assembled for this unit of work.
Contributes work that must run after the owner's own delete node.
Related-record collections delete first (they point at the owner). Embedded records delete last (the owner points at them — the FK dies with the owner row, then the peer can go). Default is a no-op.
The plan being assembled; the owner's delete node already exists.
Contributes this companion's work to the owner's save plan.
Called after the owner's own node has been added, so implementations may assume the parent node exists and order their nodes relative to it. Add nothing when there is no work — an empty contribution keeps the save on the fast single-row path.
The plan being assembled for this unit of work.
Optionaloptions: EntitySaveOptionsThe caller's save options. Implementations that decide what counts as work
(skipping clean children, most importantly) must honor flags such as
IgnoreDirtyState that demand a full write-out.
Restores this companion's state from a wire payload produced by Serialize on the other tier.
Implementations must be tolerant of payloads written by an older version of themselves — a companion is a wire contract, and rolling deploys mean both versions run at once.
The payload previously produced by Serialize.
Whether this payload is an inbound request or an authoritative result. See EntityCompanionDeserializeMode; the distinction is load-bearing.
Sync provision. Idempotent. For a required FK this is a no-op after NewRecord.
Constructs the related entity instance without NewRecord / Load.
Called from BaseEntity.InitializeEmbeddedRecords during GetEntityObject.
Entity names already being constructed, for cycle detection.
Populates this companion from the database, when it is configured to load eagerly.
Called by BaseEntity.Load() after the record's own fields are populated. Never called
from LoadFromData() — that is the row-materialization path for
RunView(ResultType:'entity_object'), so loading children there turns one view into an N+1
storm. Set-oriented eager loading is handled by RunView's batched child loading instead.
EntityName:PK tokens already on this load walk. Embedded records use it to fail a self-parented / cyclic inherit instead of recursing until the stack dies.
Called from the owner's NewRecord(). Required FKs are provisioned here so
owner.OrderID_Object is usable immediately after GetEntityObject.
Produces this companion's JSON-safe state for transport, or null when it has nothing to
send.
Returning null keeps the companion out of the payload entirely, which matters: a save that
touches only header fields should not ship an empty children array and pay for it on every
request.
'request' (default) is the caller's intent — omit a clean saved
companion so a header-only edit does not ship it. 'result' is
post-save state the client must adopt so the next save does not
re-INSERT a peer the server already persisted.
The wire payload, or null to omit this companion.
Synchronous, in-memory validation contributed by this companion.
Runs as part of the owner's Validate(), before any write, over the companion's complete
state — including pending removals. That ordering is what lets cross-child invariants such as
"debits must equal credits" be enforced correctly rather than after half the graph has landed.
Push errors onto result.Errors and set result.Success = false to fail the save.
The accumulating validation result to contribute to.
Asynchronous validation contributed by this companion — anything that needs a round trip.
The accumulating validation result to contribute to.
Unlike an entity's own ValidateAsync(), this is not governed by
BaseEntity.DefaultSkipAsyncValidation. That flag exists so an entity can opt out of its own
expensive async rules; applying it to companions silently skipped cross-child invariants,
which is how OrderEntityServer.ValidateAsync came to be dead code on every save. Companion
validation runs whenever the companion is dirty.
Internal companion for one owner-held 1:1 peer.