---
title: Update preference row
summary: Update a scoped USER_PREFERENCE row's payload and audit fields when the stored value differs from the requested value.
status: draft
owner: SAIF Team
last_reviewed: 2026-07-30
library: SAIF.Framework.Preferences
reference_type: package
operation_id: OP-PREF-005
trigger: SavePreferences calls PreferenceData.UpdatePref after DoesPrefExist returns true
---

# Update preference row

This internal write operation updates the scoped preference row only when the new payload differs from the stored payload, and refreshes the audit timestamp and update-user columns whenever it does.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-PREF-005 |
| **Trigger** | `SavePreferences` calls `UpdatePref(appName, pageName, userId, preferenceText)` after `DoesPrefExist` returns `true`. |
| **Inputs** | `appName`, `pageName`, `userId`, `preferenceText` |
| **Outputs** | `true` after the update request is issued |
| **Primary components** | `PreferenceData.UpdatePref`, `SqlHelper.ScalarQuery<int>`, `SqlHelper.NewParm` |
| **Entry point** | `SAIF.Framework.Preferences\PreferenceData.cs` |

## Steps

1. Start with the base UPDATE statement for [USER_PREFERENCE](../database-tables.md#user_preference); the WHERE clause already contains `PREFERENCE_TEXT <> :prefs`, which enforces the no-op rule [BR-004](../business-rules.md#br-004-update-is-skipped-when-stored-value-is-unchanged) through [SQL-005](../sql/SQL-005-update-preference.md).
2. Create a `SqlHelper.ScalarQuery<int>`, reset the parameter list, and bind the normalized user ID, application name, trimmed payload text, current timestamp, and `DPI.UserID` audit value ([BR-001](../business-rules.md#br-001-user-ids-are-normalized-to-lower-case), [BR-005](../business-rules.md#br-005-preference-text-is-trimmed-before-storage), [SQL-005](../sql/SQL-005-update-preference.md), [USER_PREFERENCE](../database-tables.md#user_preference)).
3. Apply page scoping to the WHERE clause ([BR-003](../business-rules.md#br-003-preference-identity-is-scoped-by-app-user-and-optional-page), [SQL-005](../sql/SQL-005-update-preference.md), [USER_PREFERENCE](../database-tables.md#user_preference)).
   - If `pageName` is empty, append `and PAGE_NAME is NULL`.
   - If `pageName` is present, append `and PAGE_NAME = :pagename` and bind the page parameter.
4. Attach the parameters and execute the scalar update through the active portal ([SQL-005](../sql/SQL-005-update-preference.md), [USER_PREFERENCE](../database-tables.md#user_preference)).
   - Create the portal request in the `SqlHelper.ScalarQuery<int>` constructor.
   - Add the bind variables to `DataRequest` through `AddParms`.
   - Set `DataRequest.RequestType` to `Scalar` and `DataRequest.SQLStatement` to the UPDATE text.
   - Call `IDataPortalInfo.ProcessPortalRequest`, then cast `DataRequest.Result` to `int` if a scalar result is returned.

   > **Origin**: Steps 4.1–4.4 are implemented by [Execute a data portal request](../../saif-framework-core/operations/execute-data-portal-request.md) (`SAIF.Framework.Core`) and executed on behalf of this operation.
5. Return `true` without inspecting the affected-row count.
   - If the stored payload already matches `:prefs`, [BR-004](../business-rules.md#br-004-update-is-skipped-when-stored-value-is-unchanged) leaves the row unchanged and this method still returns `true`.
   - On failure: portal and data-provider exceptions propagate to the caller.

## Process flow

The following diagram visualizes this operation's control flow.

```mermaid
flowchart TD
	Start([UpdatePref called]) --> Scope[Apply page-specific WHERE clause]
	Scope --> Execute[Execute guarded UPDATE]
	Execute --> Changed{Rows changed?}
	Changed -->|Yes| EndOk([Return true])
	Changed -->|No| EndNoop([Return true after no-op])
```

## Failure and edge-case behavior

- Empty `pageName` scopes the update to rows where `PAGE_NAME is NULL`.
- Unchanged payload text produces a silent no-op update because of the `PREFERENCE_TEXT <> :prefs` guard.
- The method ignores the provider result and always returns `true` after a successful execution, whether or not any row actually changed.

## 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.Framework.Preferences\PreferenceData.cs
- SAIF.Framework.Core\V1\DataAccessLayer\SqlHelper.vb
