---
title: Update or delete a maintained object
summary: Updates or deletes an existing maintenance object, verifying that exactly one row was affected by the write and reporting a contention error otherwise. This pattern is repeated identically across all seven maintenance objects in this library.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-08-17
library: SAIF.Business.Menu
reference_type: package
operation_id: OP-MENU-07
trigger: Caller calls `Save()` on a dirty existing object, or `Delete()`
---

# Update or delete a maintained object

Updates or deletes an existing maintenance object, verifying that exactly one row was affected by the write and reporting a contention error otherwise. This pattern is repeated identically across all seven maintenance objects in this library.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-MENU-07 |
| **Trigger** | Caller calls `Save()` on a dirty existing object, or `Delete()` |
| **Inputs** | Object properties (update) or key (delete), `DataPortalInfo.UserID` |
| **Outputs** | Updated or deleted row; the object returns to a "new" state after a successful delete |
| **Primary components** | Common BOF pattern, representative example `clsMenuItem`; mirrored by `clsMenuPage`, `clsMenuSecurity`, `clsMenuSecurityUser`, `clsMenuFavorite`, `clsApplicationCode`, `clsMenuCategoryCode` |
| **Entry point** | `SAIF.Business.Menu/clsMenuItem.vb` |

## Steps

1. `BeforeUpdate` issues a `NonQuery` with `UpdateSQL` ([SQL-034](../sql/SQL-034-update-menu-item.md)), passing every mutable column plus the key, and stamping `UPDATE_USER = :UserId` / `UPDATE_DATE = SYSDATE` where those columns exist. ([BR-MENU-016](../business-rules.md#br-menu-016-audit-stamp-on-insert-and-update))
2. `AfterUpdate` checks `RowsAffected <> 1`; if so it adds error `UpdateError` = `BOC.Messages.DBContentionError`, otherwise `MarkOld`. ([BR-MENU-020](../business-rules.md#br-menu-020-single-row-write-verification), [BR-MENU-022](../business-rules.md#br-menu-022-delete-returns-object-to-new-state))
   - On failure (rows affected ≠ 1): the object records the `UpdateError` message; the caller's changes remain applied in memory only.
3. `BeforeDelete` issues a `NonQuery` with `DeleteSQL` ([SQL-041](../sql/SQL-041-delete-menu-item.md)) parameterized by the key only.
4. `AfterDelete` applies the same single-row check with error key `DeleteError`, and on success calls `MarkNew` (returning the object to a transient state). ([BR-MENU-020](../business-rules.md#br-menu-020-single-row-write-verification), [BR-MENU-022](../business-rules.md#br-menu-022-delete-returns-object-to-new-state))
   - On failure (rows affected ≠ 1): the object records the `DeleteError` message and remains in its prior (old) state.

## Process flow

```mermaid
flowchart TD
    Start([Save or Delete called]) --> Action{Update or Delete?}
    Action -->|Update| Upd[BeforeUpdate: UPDATE row, stamp UPDATE_USER/UPDATE_DATE]
    Upd --> UChk{Exactly one row affected?}
    UChk -->|No| UFail([AfterUpdate: add UpdateError])
    UChk -->|Yes| UOk([AfterUpdate: MarkOld])
    Action -->|Delete| Del[BeforeDelete: DELETE row by key]
    Del --> DChk{Exactly one row affected?}
    DChk -->|No| DFail([AfterDelete: add DeleteError])
    DChk -->|Yes| DOk([AfterDelete: MarkNew])
```

## Failure and edge-case behavior
- Update affects zero or more than one row: `UpdateError` = `BOC.Messages.DBContentionError` is added; no exception is thrown.
- Delete affects zero or more than one row: `DeleteError` = `BOC.Messages.DBContentionError` is added; the object is not marked new.
- A successful delete returns the object to a "new" state, allowing it to be re-inserted without reconstructing it.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [SQL catalog](../sql/index.md)
- [Database tables](../database-tables.md)

## Sources
- SAIF.Business.Menu/clsMenuItem.vb
- SAIF.Business.Menu/clsMenuPage.vb
- SAIF.Business.Menu/clsMenuSecurity.vb
- SAIF.Business.Menu/clsMenuSecurityUser.vb
- SAIF.Business.Menu/clsMenuFavorite.vb
- SAIF.Business.Menu/clsApplicationCode.vb
- SAIF.Business.Menu/clsMenuCategoryCode.vb
