---
title: "Maintain a menu item together with its security-group assignments"
summary: "Saves a menu item and its child security-group rows as one logical change."
status: draft
owner: SAIF documentation team
last_reviewed: 2026-08-17
library: SAIF.Business.Menu
reference_type: package
operation_id: OP-MENU-003
trigger: "Caller edits a menu item and its security groups."
---

# Maintain a menu item together with its security-group assignments

Saves a menu item and its child security-group rows as one logical change.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-MENU-003 |
| **Trigger** | Caller edits a menu item and its security groups. |
| **Inputs** | `IDataPortalInfo`, menu-item fields, security groups |
| **Outputs** | Persisted item and security rows |
| **Primary components** | `clsMenuItem`, `clsMenuSecurityCollection` |
| **Entry point** | `SAIF.Business.Menu\clsMenuItem.vb` |

## Steps

1. Create a new menu-item key with [SQL-MENU-012](../sql/SQL-MENU-012-next-menu-item-sequence.md) or load an existing item with [SQL-MENU-015](../sql/SQL-MENU-015-retrieve-menu-item.md).
2. Save the parent row through [SQL-MENU-014](../sql/SQL-MENU-014-insert-menu-item.md) or [SQL-MENU-016](../sql/SQL-MENU-016-update-menu-item.md) against [MENU_ITEM](../database-tables.md#menuitem).
3. Lazy-load child security groups with [SQL-MENU-030](../sql/SQL-MENU-030-retrieve-menu-security-for-item.md) when the caller accesses `SecurityGroups`.
4. Evaluate `IsValid` and `IsDirty` so the item inherits child state from `SecurityGroups` ([BR-MENU-005](../business-rules.md#br-menu-005-menu-item-validity-and-dirtiness-cascade-to-security-groups)).
5. When the parent save succeeds and the child collection is dirty, create keys with [SQL-MENU-024](../sql/SQL-MENU-024-next-menu-security-sequence.md) and persist child rows with [SQL-MENU-026](../sql/SQL-MENU-026-insert-menu-security-row.md) or [SQL-MENU-028](../sql/SQL-MENU-028-update-menu-security-row.md) against [MENU_SECURITY](../database-tables.md#menusecurity). User-to-security-group membership rows in [MENU_SECURITY_USER](../database-tables.md#menusecurityuser) are maintained separately; see [Maintain reference data](maintain-reference-data.md).
6. Delete obsolete rows with [SQL-MENU-013](../sql/SQL-MENU-013-delete-menu-item.md) or [SQL-MENU-025](../sql/SQL-MENU-025-delete-menu-security-row.md) as required, and rely on row-count checking after every write ([BR-MENU-001](../business-rules.md#br-menu-001-optimistic-contention-on-write)).

## Process flow

The following diagram visualizes this operation's control flow.

```mermaid
flowchart TD
    Start([Caller edits menu item]) --> LoadItem[Create or retrieve item]
    LoadItem --> EditItem[Change item fields]
    EditItem --> LoadGroups[Load security groups]
    LoadGroups --> Validate[Evaluate IsValid and IsDirty]
    Validate --> PersistItem[Save MENU_ITEM]
    PersistItem --> PersistGroups[Save dirty MENU_SECURITY rows]
    PersistGroups --> Done([Return saved item])
```

## Failure and edge-case behavior

- The child `SecurityGroups` collection can keep the parent item dirty even when the parent fields did not change ([BR-MENU-005](../business-rules.md#br-menu-005-menu-item-validity-and-dirtiness-cascade-to-security-groups)).
- Unassigned items become public at read time because authorization treats missing security rows as unsecured ([BR-MENU-002](../business-rules.md#br-menu-002-unsecured-menu-items-are-visible-by-default)).

## Related documentation
- [Index](../index.md)
- [Overview](../overview.md)
- [Architecture](../architecture.md)
- [Dependencies](../dependencies.md)
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [Security and authorization](../security-authorization.md)
- [Database tables](../database-tables.md)
- [SQL catalog](../sql/index.md)
- [API reference](../api/index.md)

## Sources
- `SAIF.Business.Menu\clsMenuItem.vb`
