---
title: Manage edit session
summary: Captures, restores, and commits nested in-memory state snapshots for business objects and collections.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Framework.Core
reference_type: package
operation_id: OP-CORE-001
trigger: Caller or data binding invokes BeginEdit, CancelEdit, or ApplyEdit.
---

# Manage edit session

This operation is the framework's n-level undo workflow for business objects and business collections. It lets a caller open an edit scope, mutate object state freely in memory, and then either commit that state as the new baseline or roll it back to the last snapshot — while child objects, child collections, and deleted-item lists stay synchronized with the parent at every level.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-CORE-001 |
| **Trigger** | Caller or data binding invokes `BeginEdit`, `CancelEdit`, `ApplyEdit`, `IEditableObject.BeginEdit`, `IEditableObject.CancelEdit`, or `IEditableObject.EndEdit`. |
| **Inputs** | Current business object or collection graph, including child objects, child collections, and deleted children. |
| **Outputs** | Snapshot added, restored, or discarded; child add/delete state reconciled. |
| **Primary components** | `clsBusinessBase.BeginEdit`, `clsBusinessBase.CancelEdit`, `clsBusinessBase.ApplyEdit`, `clsBusinessCollectionBase.BeginEdit`, `clsBusinessCollectionBase.CancelEdit`, `clsBusinessCollectionBase.ApplyEdit`, `clsUndoableBase.CopyState`, `clsUndoableBase.UndoChanges`, `clsUndoableBase.AcceptChanges` |
| **Entry point** | `SAIF.Framework.Core\V1\BaseItems\clsBusinessBase.vb` |

## Steps

1. Start the edit scope by calling `BeginEdit` on a business object or collection.
   - `clsBusinessBase.BeginEdit` marks the object as being in a binding edit and calls `CopyState`.
   - `clsBusinessCollectionBase.BeginEdit` calls `CopyState` for the collection and its children.
2. Capture the current in-memory state in `clsUndoableBase.CopyState`.
   - The method reflects over instance fields, skips fields marked with `NotUndoable`, and separates child objects/collections from scalar state.
   - For each child `clsBusinessBase` or `clsBusinessCollectionBase`, it cascades `CopyState()` so the full object graph participates in the same edit level.
   - For ordinary fields, it stores `<DeclaringType>!<FieldName>` keys and field values in a hashtable.
   - Before serialization, it raises serialization-notification hooks for captured state; it then serializes the hashtable into the state stack.
3. Cancel the edit scope by calling `CancelEdit` when changes should be rolled back.
   - `clsBusinessBase.CancelEdit` clears the binding-edit flag, calls `UndoChanges()`, re-registers rules through `AddBusinessRules()`, and raises `OnIsModifiedChanged()`.
   - `clsBusinessCollectionBase.CancelEdit` calls `UndoChanges()` and then re-runs `AddBusinessRules()` for both current children and deleted children.
4. Restore the last snapshot in `UndoChanges`.
   - `clsUndoableBase.UndoChanges` pops the latest serialized snapshot, deserializes it, and restores ordinary field values.
   - For child objects and child collections, it cascades `UndoChanges()` instead of replacing references directly.
   - Collection `UndoChanges` decrements the collection edit level, removes items that were added below the restored level, and moves rolled-back deleted children back into the live list when their delete flag clears.
   - If data binding cancels a brand-new child before it is ever committed, `IEditableObject.CancelEdit` removes that child from its parent collection entirely.
5. Commit the edit scope by calling `ApplyEdit` when changes should become the new baseline.
   - `clsBusinessBase.ApplyEdit` clears the binding-edit flag, marks the object as committed, and calls `AcceptChanges()`.
   - `clsBusinessCollectionBase.ApplyEdit` calls `AcceptChanges()` for the collection graph.
6. Remove the most recent snapshot in `AcceptChanges`.
   - `clsUndoableBase.AcceptChanges` pops the latest state-stack entry.
   - It then cascades `AcceptChanges()` through child objects and child collections so every participant discards the same edit level.

## Process flow

The following diagram visualizes this operation's control flow.

```mermaid
flowchart TD
	Start([BeginEdit]) --> Copy[Copy current state]
	Copy --> Change[Caller changes object graph]
	Change --> D{Commit or cancel?}
	D -->|Cancel| Undo[UndoChanges and rebuild rules]
	D -->|Apply| Accept[AcceptChanges]
	Undo --> EndUndo([State restored])
	Accept --> EndOk([State committed])
```

## Failure and edge-case behavior

- Repeated `IEditableObject.CancelEdit` or `IEditableObject.EndEdit` calls are no-ops once `m_BindingEdit` is no longer active, so double-cancel from data-binding infrastructure cannot corrupt state.
- Canceling a brand-new bound child removes it from the parent collection entirely when rollback returns below the level at which it was added — the child never existed as far as the committed graph is concerned.
- Fields marked `NotUndoable` are excluded from every snapshot, so undo never restores them; callers relying on undo for those fields will not see the expected rollback.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

## Sources
- SAIF.Framework.Core\V1\BaseItems\clsBusinessBase.vb
- SAIF.Framework.Core\V1\BaseItems\clsBusinessCollectionBase.vb
- SAIF.Framework.Core\V1\BaseItems\clsUndoableBase.vb
