---
title: Retrieve provider aggregate
summary: Loads a provider header row and exposes its lazily loaded location and tax ID collections through the BOF data portal.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Business.Provider
reference_type: package
operation_id: OP-PRV-01
trigger: Caller invokes clsProvider.GetProvider(dpi, providerSeqNum)
---

# Retrieve provider aggregate

`clsProvider` is a thin header object keyed by `ProviderSeqNum` that stores only the primary tax ID pointer and audit fields; the bulk of a provider's data — locations, tax IDs, and everything hanging off a location — is exposed through read-only collection properties that retrieve on first access rather than eagerly with the header.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-PRV-01 |
| **Trigger** | Caller invokes `clsProvider.GetProvider(inDataportal, inProviderSeqNum)` |
| **Inputs** | `ProviderSeqNum`, `IDataPortalInfo` |
| **Outputs** | Populated `clsProvider` with `PrimaryTaxID`, audit fields, and lazily loaded `Locations`/`TaxIds` collections |
| **Primary components** | `clsProvider.GetProvider`, `clsProvider.BeforeRetrieve`, `clsProvider.AfterRetrieve`, `clsProviderLocationCollection.GetProviderLocationCollection`, `clsProviderTaxIDCollection.GetProviderTaxIDCollection` |
| **Entry point** | `Business\SAIF.Business.Provider\clsProvider.vb` |

## Steps

1. **Caller requests the provider.** `clsProvider.GetProvider(inDataportal, inProviderSeqNum)` constructs a `Criteria` with the `ProviderSeqNum` key and calls `inDataportal.DataAccessLayer.RetrieveObject(...)`.
2. **Retrieve the header row.** `BeforeRetrieve` issues a result-set request against [SQL-042](../sql/SQL-042-retrieve-provider.md), selecting `PROVIDER_SEQ_NUM`, `PRIMARY_TAX_ID`, and audit columns from [PROVIDER](../database-tables.md#provider) filtered by `:ProviderSeqNum`.
3. **Fill the object.** `AfterRetrieve` fills `PrimaryTaxID` and audit fields from the first returned row (if any) and marks the object clean via `MarkOld()`.
   - If no row matches, the object is still marked old but its fields remain at their initial (empty) values; the caller receives an unpopulated object rather than an explicit not-found error.*
4. **Resolve the registered name for legacy compatibility.** The `RegisteredName` read-only property scans `TaxIds` for the row with `PrimaryTaxIDFlag = "Y"` and returns its `RegisteredName`, or an empty string if none is flagged primary.
5. **Load locations on demand.** The first access to `Locations` calls `clsProviderLocationCollection.GetProviderLocationCollection(MyCriteria.DataPortalInfo, m_ProviderSeqNum)`, which retrieves every [PROVIDER_LOCATION](../database-tables.md#provider_location) row for the provider ([SQL-053](../sql/SQL-053-retrieve-provider-location.md)) and caches the collection for the life of the object.
   - Each returned `clsProviderLocation` can, in turn, be used to load its own address, contact, identity, tax ID, relationship, service, MCO membership, ineligibility period, DCBS cost/charge, and note children — each of which is documented as its own maintenance operation.
6. **Load tax IDs on demand.** The first access to `TaxIds` calls `clsProviderTaxIDCollection.GetProviderTaxIDCollection(MyCriteria.DataPortalInfo, m_ProviderSeqNum)`, which retrieves every [PROVIDER_TAX_ID](../database-tables.md#provider_tax_id) row for the provider ([SQL-060](../sql/SQL-060-retrieve-provider-tax-i-d.md)) and caches the collection.
7. **Create a new provider (less common path).** `NewProvider(inDataportal)` calls `inDataportal.DataAccessLayer.CreateObject(...)`, and `Create()` assigns `m_ProviderSeqNum` from `GetNextSeqNum("PROVIDER_SEQ")` — the caller is then expected to populate a location and tax ID before the aggregate is meaningful.
8. **Persist header changes.** `BeforeInsert`/`BeforeUpdate` write `PrimaryTaxID`, `ProviderSeqNum`, and audit fields to [PROVIDER](../database-tables.md#provider) ([SQL-018](../sql/SQL-018-insert-provider.md), [SQL-066](../sql/SQL-066-update-provider.md)); the update statement enforces optimistic concurrency on `UPDATE_NUM`.
   - On a rows-affected value other than `1`: insert adds `"Insert Failed"`; update adds the DB contention error message.

## Process flow

```mermaid
flowchart TD
	Start([Caller calls GetProvider]) --> Retrieve[Retrieve header row from PROVIDER]
	Retrieve --> Found{Row found?}
	Found -->|Yes| Fill[Fill PrimaryTaxID and audit fields]
	Found -->|No| Empty([Unpopulated object returned])
	Fill --> Access{Caller accesses Locations or TaxIds?}
	Access -->|Locations| LoadLoc[Lazily retrieve PROVIDER_LOCATION collection]
	Access -->|TaxIds| LoadTax[Lazily retrieve PROVIDER_TAX_ID collection]
	Access -->|Neither| Done([Header-only object returned])
	LoadLoc --> Cached([Collection cached on object])
	LoadTax --> Cached
```

## Failure and edge-case behavior

- A `ProviderSeqNum` with no matching row returns an unpopulated object rather than throwing; callers must check the populated state themselves.*
- `Locations` and `TaxIds` are cached only for the lifetime of the in-memory object — no cross-request caching occurs.*
- `RegisteredName` silently returns an empty string when no tax ID is flagged primary, rather than surfacing an error.
- Header insert/update failures follow the standard rows-affected pattern: a value other than `1` produces an error message instead of a thrown exception.

## Related documentation

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

> **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.Provider\clsProvider.vb`
- `Business\SAIF.Business.Provider\clsProviderLocationCollection.vb`
- `Business\SAIF.Business.Provider\clsProviderTaxIDCollection.vb`
