Skip to content

Menu tree construction

Menu tree construction builds and maintains the two-level hierarchy that the presentation layer renders as the application menu: clsMenuPage records group toolbar buttons and links under an application, and clsMenuItem records are the individual tree nodes assigned to a page and a category. Both objects follow the standard BOF CRUD lifecycle — retrieve by key or in full, insert with a sequence-generated key, update, and delete — each governed by the row-count contention check.

Field Value
Operation ID OP-MNU-01
Trigger Caller requests clsMenuItem/clsMenuItemCollection or clsMenuPage/clsMenuPageCollection by key or in full, or calls Save() after Create()/retrieve
Inputs MenuItemSeqNum / MenuPageSeqNum (key lookups); for insert/update — page/category association, tooltip text, visibility flag, page order, application code, page link, and toolbar-button flags/tooltips
Outputs Populated clsMenuItem/clsMenuItemCollection or clsMenuPage/clsMenuPageCollection; on save, a new sequence-generated key or updated row; error messages on DML contention
Primary components clsMenuItem, clsMenuItemCollection, clsMenuPage, clsMenuPageCollection
Entry point Business\SAIF.Business.Menu\clsMenuItem.vb, Business\SAIF.Business.Menu\clsMenuPage.vb

Steps

  1. Retrieve a single menu item. clsMenuItem.GetMenuItem creates a Criteria with the requested MenuItemSeqNum and invokes the data portal, which runs BeforeRetrieve to bind :MenuItemSeqNum and execute RetrieveSQL (SQL-022, MENU_ITEM). AfterRetrieve fills the object from the reader and marks it old.
  2. MenuPage and MenuCategory are lazy-loaded read-only properties: the first access triggers clsMenuPage.GetMenuPage (SQL-024) or clsMenuCategoryCode.GetMenuCategoryCode respectively.
  3. SecurityGroups is likewise lazy-loaded via clsMenuSecurityCollection.GetMenuSecuritiesForItem (SQL-027).
  4. Retrieve the full item collection. clsMenuItemCollection.GetAllMenuItems executes the unfiltered RetrieveSQL (SQL-023, MENU_ITEM) and loads one clsMenuItem per row via AfterRetrieve.
  5. Retrieve a single page or the full page collection. clsMenuPage.GetMenuPage and clsMenuPageCollection.GetAllMenuPages follow the identical by-key (SQL-024) and all-rows (SQL-025) pattern against MENU_PAGE.
  6. Create a new menu item. clsMenuItem.NewMenuItem calls Create(), which invokes BeforeCreate/AfterCreate: a scalar request executes SELECT MENU_ITEM_SEQ.NEXTVAL FROM DUAL (SQL-002, DUAL), and the returned value seeds MenuItemSeqNum on both the object and its criteria before the object is marked new and clean.
  7. Save (insert) the new item. IsValid checks MyBase.IsValid and the validity of the lazily-loaded SecurityGroups child collection before Save()/SaveChild() proceeds. BeforeInsert binds the page, category, visible flag, page order, tooltip, and the acting user (DataPortalInfo.UserID, upper-cased), then executes InsertSQL (SQL-008, MENU_ITEM).
  8. On success (RowsAffected = 1): AfterInsert calls MarkOld() and, if SecurityGroups.IsDirty, SaveChildObject(SecurityGroups) cascades the save to the child security collection (BR-MNU-02).
  9. On failure (RowsAffected <> 1): Me.AddErrorMessage("InsertError", "Insert Failed") is added; the save is rejected.
  10. Save (insert) a new menu page. clsMenuPage.NewMenuPage / AfterCreate obtains MENU_PAGE_SEQ.NEXTVAL (SQL-003, DUAL); BeforeInsert assembles the insert with StringBuilder across all page fields (page name, link, application code, favorite name, and every toolbar flag/tooltip) and executes InsertSQL (SQL-009, MENU_PAGE).
  11. On a rows-affected value other than 1: AddErrorMessage("InsertError", ...) (BR-MNU-02).
  12. Update an existing item or page. BeforeUpdate rebinds all mutable fields plus the primary key and executes UpdateSQL (SQL-034 for items, SQL-035 for pages). AfterUpdate applies the same row-count contention check, calling MarkOld() on success or AddErrorMessage("UpdateError", BOC.Messages.DBContentionError) on failure (BR-MNU-02).
  13. Delete an item or page. BeforeDelete binds the key and executes DeleteSQL (SQL-041 for items, SQL-042 for pages). AfterDelete calls MarkNew() on a rows-affected value of 1, or adds a DeleteError otherwise (BR-MNU-02).
  14. Assemble the tree. The presentation layer combines the retrieved item and page collections — joining items to their parent page via MenuPageSeqNum and to their category via MenuCategoryCode — to render the hierarchical menu. This assembly step is performed by the consuming presentation layer, not within this library.*

Process flow

The following diagram visualizes the retrieve/save control flow shared by clsMenuItem and clsMenuPage.

flowchart TD
    Start([Caller requests item/page<br>by key, all, or new]) --> Which{Operation?}
    Which -->|Retrieve by key| RetKey[Execute RetrieveSQL<br>with key parameter]
    RetKey --> Fill[Fill object from reader;<br>MarkOld]
    Which -->|Retrieve all| RetAll[Execute unfiltered RetrieveSQL]
    RetAll --> FillEach[Load one object per row]
    Which -->|Create new| Seq[Obtain NEXTVAL from<br>MENU_ITEM_SEQ / MENU_PAGE_SEQ]
    Seq --> MarkNewClean[Seed key; MarkNew; MarkClean]
    MarkNewClean --> SaveCall{Save called?}
    SaveCall -->|Yes| Insert[Execute InsertSQL]
    Insert --> Rows{RowsAffected = 1?}
    Rows -->|Yes| Ok([MarkOld; cascade child save])
    Rows -->|No| Err([AddErrorMessage: InsertError])

Failure and edge-case behavior

  • Every insert, update, and delete on clsMenuItem and clsMenuPage enforces a strict rows-affected-equals-1 check; any other value raises InsertError, UpdateError, or DeleteError and the object is not marked clean (BR-MNU-02).
  • clsMenuItem.IsValid and IsDirty both cascade into the lazily-loaded SecurityGroups child collection, so a dirty or invalid security-group child blocks or flags the parent item even if the item's own fields are unchanged.
  • The child SecurityGroups collection is only saved when SecurityGroups.IsDirty is true and the parent insert/update succeeded; if the parent DML fails, the child save is skipped entirely.
  • clsMenuPage's insert/update SQL is assembled with StringBuilder across roughly twenty columns; a mismatch between the assembled parameter list and the bound parameters would surface only at execution time, not through compile-time checking.*
  • Presentation-layer tree assembly (joining items to pages and categories) is not implemented in this library; if the page or category referenced by an item's foreign key is deleted independently, the tree assembly step must handle the resulting orphaned reference.*

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts.

Sources

  • Business\SAIF.Business.Menu\clsMenuItem.vb
  • Business\SAIF.Business.Menu\clsMenuItemCollection.vb
  • Business\SAIF.Business.Menu\clsMenuPage.vb
  • Business\SAIF.Business.Menu\clsMenuPageCollection.vb