OptionalGraphCycle guard: keys of the records already being persisted higher up in this unit of work.
Set by the graph executor and threaded down through each child's Save() — that hop is why
it lives on the options rather than staying inside the plan. A self-referential collection
(SubAgents on MJ: AI Agents via ParentID, say) can otherwise recurse until the call
stack dies, which surfaces as an unattributable crash instead of a fixable error.
Not something callers set. Its lifetime is exactly one unit of work, which is deliberate — a process-global would be shared across concurrent requests and would report cycles that are really just two requests touching the same record at once.
If set to true, the record will be saved to the database even if nothing is detected to be "dirty" or changed since the prior load.
OptionalISAThe entity name of the child that initiated this parent save in an IS-A chain. Used by server-side providers to skip the active branch when propagating Record Change entries to sibling branches of overlapping parents. Only set when IsParentEntitySave is true.
OptionalIsWhen true, this entity is being saved as part of an IS-A parent chain initiated by a child entity. Provider behavior:
OptionalOnOptional callback invoked exactly once, after all pre-flight checks pass
(synchronous Validate(), ValidateAsync(), and PreSave hooks) but before the
record is persisted to the database. It receives the entity being saved.
This is the framework hook for optimistic UI: a UI surface can render the user's
change the moment it is known to be valid — without the "render then validation fails
then roll back" flicker you get when you render before calling Save(). The await on
Save() is still in place; only the visible render moves earlier.
It does not fire when the save is skipped (not dirty / ReplayOnly) or when validation
fails. Any error thrown by the callback is swallowed and logged so a UI bug can never
abort the persistence it was meant to accompany. See guides/OPTIMISTIC_UI_SAVE_PATTERN.md.
Receives the BaseEntity being saved (callers typically close over their own entity
reference and ignore the parameter).
OptionalOriginatingIDs of the Entity Actions that caused this save — set by code writing back on behalf of one. Those actions will not be re-fired by this save's after-save hooks.
This is the loop-breaker for enrich-and-write-back automations: an action running on
AfterUpdate that stores its result on the same record would otherwise re-trigger itself
forever. In-process that is detected automatically (the dispatch guard tracks origin through
the async call tree), so this exists for work that has detached — a task graph executed
later by the durable dispatcher, a queued job — where the ambient origin is long gone and the
write-back is otherwise indistinguishable from a user's edit.
Only after-save invocations are skipped. Validate and Before* still run: whether a record
is legal does not depend on who is saving it.
OptionalReplayWhen set to true, the save operation will BYPASS Validate() and the actual process of saving changes to the database but WILL invoke any associated actions (AI Actions, Entity Actions, etc...) Subclasses can also override the Save() method to provide custom logic that will be invoked when ReplayOnly is set to true
OptionalSkipWhen set to true, the entity will skip the asynchronous ValidateAsync() method during save. This is an advanced setting and should only be used when you are sure the async validation is not needed. The default behavior is to run the async validation and the default value is undefined. Also, you can set an Entity level default in a BaseEntity subclass by overriding the DefaultSkipAsyncValidation() getter property.
OptionalSkipIf set to true, any Entity Actions associated with invocation types of Create or Update will be skipped during the save operation
OptionalSkipIf set to true, an AI actions associated with the entity will be skipped during the save operation
OptionalSkipWhen true, the save skips the geocoding side trip even when the entity has
SupportsGeoCoding on. Same rationale and scoping as SkipRecordChanges: a synced
record arrives pre-formed from the source system and does not need a per-write geocode
lookup, while a human's edit to an address should still trigger one.
OptionalSkipSetting this to true means that the system will not look for inconsistency between the state of the record at the time it was loaded and the current database version of the record. This is normally on because it is a good way to prevent overwriting changes made by other users that happened after your version of the record was loaded. However, in some cases, you may want to skip this check, such as when you are updating a record that you know has not been changed by anyone else since you loaded it. In that case, you can set this property to true to skip the check which will be more efficient.
OptionalSkipWhen true, the save skips writing a Record Change (audit) row even when the entity has
TrackRecordChanges on.
This exists for high-volume MACHINE writes — an integration sync applying tens of thousands of records in minutes — where the audit row is a per-write cost with no value: the "who" is always the sync, and the real history lives in the source system. Scoping the suppression to the save (instead of turning the entity flag off) keeps the capability for every other writer: a human editing the same record through the UI is still audited, because their save never sets this option.
OptionalSkipPersist owner-held embeds (and other non-collection companions) but skip RelatedRecordCollection nodes.
Use this when the caller will write the collections itself after preparing them (pricing, expansion, sequence). The graph executor's recursion guard is private on BaseEntity — it is not a caller-facing "header-only" switch.
Save options used when saving an entity record. Provides fine-grained control over the save operation including validation, action execution, and conflict detection.