Skip to content

Multi-database portal registration

ApplicationDataAccess is the multi-portal registration helper that an AppManagerBase subclass uses (typically from its OnCreateDataLayer() hook) to build one or more keyed IDataPortalInfo entries — one per logical database connection the manager needs — backed by either a SQL Server or an Oracle data portal.

Field Value
Operation ID OP-APP-05
Trigger Consuming code sets DBUser, Database, Server, and/or BatchID on an ApplicationDataAccess instance and calls CreateSQLServerPortal(key) or CreateOraclePortal(key)
Inputs DBUser, Database, Server, BatchID connection properties; the registration key string; optionally a batch user ID supplied to the constructor
Outputs A stored, keyed IDataPortalInfo entry retrievable through the DPI(key) property; the underlying clsDataAccess (DAL) instance used to add the portal
Primary components ApplicationDataAccess.New, ApplicationDataAccess.CreateSQLServerPortal, ApplicationDataAccess.CreateOraclePortal, ApplicationDataAccess.DPI, AppManagerBase.InjectDPI
Entry point Application\SAIF.Application\ApplicationDataAccess.vb

Steps

  1. Construct the data-access object. New(Optional inBatchUserID As String = "") initializes a fresh clsDataAccess.clsEnvironment and either:
  2. constructs clsDataAccess(inBatchUserID) and sets m_Env.ClientID = inBatchUserID when a non-blank batch user ID is supplied, or
  3. constructs the parameterless clsDataAccess and sets m_Env.ClientID = m_DAL.ClientID from the resulting DAL when no batch user ID is given.
  4. This constructor is normally invoked indirectly, through AppManagerBase.DataLayer's lazy getter, which passes SecurityPrincipal.UserName.ToString as the batch user ID — see Application context initialization.
  5. Set connection properties. Consuming code (typically the subclass's OnCreateDataLayer()) sets DBUser, Database, Server, and optionally BatchID on the ApplicationDataAccess instance before registering any portal. These are plain auto-implemented properties with no validation.
  6. Register a SQL Server portal. CreateSQLServerPortal(inKey As String):
  7. Copies m_DAL.ClientID into m_Env.ClientID.
  8. Copies DBUser, Database, and Server into the environment.
  9. Sets m_Env.DBMSType = clsDataAccess.clsEnvironment.DBMS.SqlServer.
  10. If BatchID.Trim.Length > 0: overwrites m_Env.ClientID with BatchID, so a configured batch ID takes precedence over the DAL's own client ID (BR-005).
  11. Constructs New clsSQLDataPortal(m_Env, True) and adds it to m_DPILibrary under the lower-cased key, via DAL.AddDataPortal(portal, portal.PortalID & inKey).
  12. On failure: this method calls m_DPILibrary.Add(inKey.ToLower, ...) directly rather than through the DPI property setter, so it does not perform the duplicate-key guard that the DPI setter enforces — a second call with the same key raises a runtime "key already exists" exception from the underlying SortedList instead of being silently ignored.*
  13. Register an Oracle portal. CreateOraclePortal(inKey As String) follows the same sequence as step 3 but sets m_Env.DBMSType = clsDataAccess.clsEnvironment.DBMS.Oracle, omits the Server assignment (Oracle connections are identified by Database and DBUser only in this environment shape), and constructs New clsMSOracleDataPortal(m_Env) instead of the SQL Server portal type (BR-005).
  14. Same direct-Add caveat as step 3 applies — a duplicate key raises an exception rather than being ignored.*
  15. Retrieve a registered portal. Any caller reads DPI(key) to get the stored IDataPortalInfo, which looks the key up by its lower-cased form in m_DPILibrary.
  16. On failure: requesting a key that was never registered raises a KeyNotFoundException from the underlying SortedList, since the getter performs no existence check.*
  17. Inject a portal without going through Create*Portal. AppManagerBase.InjectDPI(inKey, inDPI) sets DataLayer.DPI(inKey) = inDPI directly, which — unlike steps 3–4 — routes through the DPI property's Friend setter. That setter lower-cases the key and only adds the entry when ContainsKey is False, so an injected key that already exists is silently ignored rather than replacing the prior entry or raising (BR-004).

Process flow

The following diagram visualizes portal registration for both database types and the two different registration entry points.

flowchart TD
    Start([ApplicationDataAccess constructed]) --> SetProps[Set DBUser, Database,<br>Server, BatchID]
    SetProps --> Which{SQL Server or Oracle?}
    Which -->|SQL Server| SqlEnv[Copy ClientID, DBUser,<br>Database, Server; set DBMS=SqlServer]
    Which -->|Oracle| OraEnv[Copy ClientID, DBUser,<br>Database; set DBMS=Oracle]
    SqlEnv --> BatchCheck1{BatchID set?}
    OraEnv --> BatchCheck2{BatchID set?}
    BatchCheck1 -->|Yes| OverrideClient1[ClientID = BatchID]
    BatchCheck1 -->|No| KeepClient1[Keep DAL ClientID]
    BatchCheck2 -->|Yes| OverrideClient2[ClientID = BatchID]
    BatchCheck2 -->|No| KeepClient2[Keep DAL ClientID]
    OverrideClient1 --> BuildSql[New clsSQLDataPortal]
    KeepClient1 --> BuildSql
    OverrideClient2 --> BuildOra[New clsMSOracleDataPortal]
    KeepClient2 --> BuildOra
    BuildSql --> AddDirect[m_DPILibrary.Add key,<br>duplicate key throws]
    BuildOra --> AddDirect
    AddDirect --> Stored([Portal available via DPI key]))
    InjectPath([AppManagerBase.InjectDPI]) --> Setter[DPI setter: add only<br>if key not already present]
    Setter --> Stored

Failure and edge-case behavior

  • CreateSQLServerPortal and CreateOraclePortal add to m_DPILibrary directly instead of through the guarded DPI property setter, so registering the same key twice through either Create*Portal method throws, while re-injecting the same key through InjectDPI is silently ignored — the two registration paths are inconsistent with each other (BR-004).*
  • Neither CreateSQLServerPortal nor CreateOraclePortal validates that DBUser, Database, or Server (for SQL Server) are non-blank before building the portal — a portal can be constructed from an empty connection environment with no explicit error from this library.*
  • CreateOraclePortal never sets m_Env.Server, so any previously-set Server value on the shared m_Env.clsEnvironment instance persists across a mixed sequence of SQL Server and Oracle portal creation calls on the same ApplicationDataAccess instance, since m_Env is reused rather than recreated per call.*
  • Reading DPI(key) for an unregistered key raises a KeyNotFoundException; there is no TryGetValue-style accessor exposed by this class.*

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

Sources

  • Application\SAIF.Application\ApplicationDataAccess.vb
  • Application\SAIF.Application\AppManagerBase.vb