Skip to content

Log an application error

Callers record an application error by calling one of the two CreateNewAppError factory overloads on clsAppError — one that accepts a framework ErrorInfoStruct and one that accepts discrete field values. Both overloads populate the same set of fields on a new clsAppError instance and immediately call Save(), so both converge on the identical insert-and-identity-retrieval lifecycle.

Field Value
Operation ID OP-LV-01
Trigger Caller calls clsAppError.CreateNewAppError(inDPI, inErrorStruct) or clsAppError.CreateNewAppError(inDPI, inAppName, inEnvironment, inErrorDate, inErrorInfo, inErrorSource, inMachineName, inPageName, inStateInfo, inUserName)
Inputs IDataPortalInfo; either a SAIF.Framework.Logging.ErrorInfoStruct or the nine discrete fields (AppName, Environment, ErrorDate, ErrorInfo, ErrorSource, MachineName, PageName, StateInfo, UserName)
Outputs Saved clsAppError instance with AppErrorSeqNum populated; an InsertError business-object message if the insert did not affect exactly one row
Primary components clsAppError.CreateNewAppError (both overloads), clsAppError.BeforeInsert, clsAppError.AfterInsert
Entry point Application\SAIF.Application.LogView\clsAppError.vb

Steps

  1. Allocate a new object. Both CreateNewAppError overloads build a clsAppError.Criteria from the supplied IDataPortalInfo and call inDPI.DataAccessLayer.CreateObject(Crit), which runs the BOF Create() lifecycle method. Create() seeds all fields to empty strings and sets ErrorDate to Now before the factory overwrites them.
  2. Populate fields from the caller's input.
  3. Struct overload: copies ApplicationName, Environment, ErrorDate, ExceptionInfo, ExceptionSource, MachineName, PageName, StateInfo, and Username from the ErrorInfoStruct onto the corresponding clsAppError properties.
  4. Discrete-parameter overload: copies the nine caller-supplied arguments directly onto the same set of properties.
  5. Each property setter calls MarkDirty() when the incoming value differs from the current value, which is what makes the object savable.
  6. Save the object. Both overloads call temp.Save() immediately after populating fields, entering the BOF save lifecycle for a new (dirty, not-yet-persisted) object, which routes to BeforeInsert/AfterInsert rather than update.
  7. Build the insert request (BeforeInsert). A NonQuery data request is configured with MyCriteria.InsertSQL (SQL-003, App_Error) and bound parameters @App_Name, @Error_Date (or a DbType.DateTime null placeholder when ErrorDate is Date.MinValue), @Environment, @Machine_Name, @Page_Name, @Error_Source, @State_Info, @Error_Info, and @User_Name.
  8. A second scalar data request is queued in the same call with SQLStatement = "Select @@IDENTITY" (SQL-001) to retrieve the generated sequence number in the same round trip.
  9. Evaluate the insert result (AfterInsert). The rows-affected value from the first data request is read.
  10. If RowsAffected <> 1: add the InsertError business-object message (BR-006) and leave AppErrorSeqNum unset — the object is not marked old, so it still reports as new/dirty.
  11. Otherwise: read the @@IDENTITY scalar result from the second data request, cast it to Integer, assign it to m_AppErrorSeqNum, and call MarkOld() to mark the object as persisted and clean.
  12. Return the saved object. Both factory overloads return the same temp reference used throughout, now populated with AppErrorSeqNum on success.

Process flow

The following diagram visualizes the two-overload create-and-save flow.

flowchart TD
    Start([Caller calls CreateNewAppError]) --> Which{Struct or discrete<br>parameter overload?}
    Which -->|Struct| MapStruct[Map ErrorInfoStruct fields<br>to clsAppError properties]
    Which -->|Discrete| MapDiscrete[Map discrete arguments<br>to clsAppError properties]
    MapStruct --> Save[Call Save]
    MapDiscrete --> Save
    Save --> Insert[BeforeInsert: build InsertSQL<br>and @@IDENTITY requests]
    Insert --> Exec[Execute insert and<br>identity scalar request]
    Exec --> Rows{RowsAffected = 1?}
    Rows -->|No| Err([AddErrorMessage InsertError;<br>AppErrorSeqNum unset])
    Rows -->|Yes| Identity[Read @@IDENTITY,<br>set AppErrorSeqNum, MarkOld]
    Identity --> Done([Saved clsAppError returned])

Failure and edge-case behavior

  • If ErrorDate is left at Date.MinValue (never assigned), BeforeInsert binds @Error_Date as an explicit DbType.DateTime null rather than a value, so the row is inserted with a null error date.
  • A RowsAffected value other than 1 — including 0 or more than 1 — is treated uniformly as an insert failure; AppErrorSeqNum remains unset and the object is not marked old, so a subsequent Save() would attempt the insert again.
  • AfterInsert does not validate IsValid before proceeding, and AddBusinessRules() on clsAppError registers no rules, so no field-level validation blocks the insert.*

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.LogView\clsAppError.vb