Skip to content

Save business object

This operation is the core persistence pipeline for a single business object, whether it is a root object saved directly or a child object saved on behalf of a parent. It validates the object's entry state, re-runs rule evaluation, picks the correct delete, insert, or update branch purely from the object's own state flags (IsDeleted, IsNew, IsDirty), and then drives the matching before/after hook sequence on the data-access layer.

Field Value
Operation ID OP-CORE-005
Trigger Caller invokes Save() on a root object or parent logic invokes SaveChild() on a child object.
Inputs Business object state flags, current broken-rule state, CriteriaObject, and DataPortalInfo.
Outputs Successful delete/insert/update/no-op result, False when errors remain after persistence, or thrown exception for invalid entry states.
Primary components clsBusinessBase.Save, clsBusinessBase.SaveChild, clsBusinessBase.ExecuteData, clsDataAccess.DeleteObject, clsDataAccess.InsertObject, clsDataAccess.UpdateObject
Entry point SAIF.Framework.Core\V1\BaseItems\clsBusinessBase.vb

Steps

  1. Enter through Save() for a root object or SaveChild() for a child object.
  2. Root save rejects child objects; child save rejects non-child objects.
  3. Both entry points reject read-only objects and open edit levels before persistence starts.
  4. Re-evaluate validity before saving (BR-001, BR-002).
  5. If IsValid = False, the method throws ApplicationException that includes the current IDataErrorInfo.Error text (BR-001, BR-002).
  6. Clear current errors and warnings before data work begins.
  7. Rules.BrokenRules.ClearErrorsAndWarnings() resets the transient error/warning surface so the next persistence attempt starts clean (BR-002).
  8. Branch inside ExecuteData() based on object state.
  9. If IsDeleted = True and IsNew = True, the method returns True immediately because nothing exists to remove.
  10. If IsDeleted = True and IsNew = False, the method delegates to DeleteObject(Me).
  11. If IsNew = True and IsDirty = True, the method delegates to InsertObject(Me).
  12. If IsDirty = True and IsNew = False, the method delegates to UpdateObject(Me).
  13. If the object is not dirty, the method returns True without any data-access work.
  14. Run the delete, insert, or update hook sequence inside clsDataAccess.
  15. OnSave() runs first.
  16. The data-access layer resolves the portal from CriteriaObject.DataPortalInfo.PortalID, creates a new portal request, and calls the matching BeforeDelete, BeforeInsert, or BeforeUpdate hook.
  17. If one or more requests were queued, the portal connects, processes the batch, runs the matching After* hook, and closes any reader results.
  18. The portal disconnects in Finally, and OnSaveComplete() runs after the persistence method finishes.
  19. Translate post-persistence errors into the boolean result (BR-002).
  20. After each delete/insert/update branch, ExecuteData() returns False when ErrorCount > 0.
  21. Otherwise it returns True.

Process flow

The following diagram visualizes this operation's control flow.

flowchart TD
    Start([Save or SaveChild]) --> Valid{Valid save state?}
    Valid -->|No| EndErr([Throw ApplicationException])
    Valid -->|Yes| Deleted{Deleted?}
    Deleted -->|Yes and new| EndNoop([Return true])
    Deleted -->|Yes and existing| Delete[DeleteObject]
    Deleted -->|No| NewDirty{New and dirty?}
    NewDirty -->|Yes| Insert[InsertObject]
    NewDirty -->|No| Dirty{Dirty?}
    Dirty -->|Yes| Update[UpdateObject]
    Dirty -->|No| EndNoop
    Delete --> Finish[Return false if errors remain]
    Insert --> Finish
    Update --> Finish
    Finish --> EndOk([Return save result])

Failure and edge-case behavior

  • A root object cannot call SaveChild() and a child object cannot call Save() — each entry point enforces the opposite role and throws ApplicationException immediately.
  • An invalid object fails before any data-access work starts, and the thrown exception carries the current IDataErrorInfo.Error text so the caller sees exactly which rule blocked the save.
  • A deleted object that was never persisted (IsDeleted = True and IsNew = True) is treated as a successful no-op, since there is nothing in the data store to remove.
  • Data-access failures from the delete, insert, or update helpers are wrapped in ApplicationException.

Sources

  • SAIF.Framework.Core\V1\BaseItems\clsBusinessBase.vb
  • SAIF.Framework.Core\V1\DataAccessLayer\clsDataAccess.vb