What is a Logical Unit of Work
In a transactional system, there are some sets of work that you want saved either in its entirety, or not at all. For example, you don’t want detail lines of an order to be successfully saved if the save of the header row containing order total information fails. You probably don’t want a users permissions deleted if the delete of the user fails. So, a logical unit of work is a set of transactions where either all are successfully applied against the database, or none have any impact on the database.
How does PFC's LUW Service work
Each LUW object keeps a list of objects that make up the logical unit of work. In strict PFC environments, all objects in this list will be self-updating objects. However, the LUW can handle certain non-PFC and non self-updating objects. The LUWs concept of save functionality is broken down into several stages:
- AcceptText
- UpdatesPending
- Validation
- UpdatePrep
- PreUpdate
- BeginTran (LUW oriented not applied to objects)
- Update
- EndTran (LUW oriented not applied to objects)
- PostUpdate
(Details of these stages are described in the table below.)
For each stage, appropriate logic is applied against each object in the list before proceeding with the next stage. If any object fails to pass one of these stages, the entire process is aborted.
For example, all objects will have to pass the AcceptText stage. If one of the objects fails the AcceptText stage, no further AcceptTexts are performed and no further stages are processed.
What are Self-Updating Objects
Simply put, self-updating objects (SUOs) are objects that know how to save (or update) themselves. They all have code that implements the Update stage of the Logical Unit of Work service, and may contain code to implement other stages. To implement functionality for each of the save process stages, the Logical Unit of Work service expects a specific API. (For details on the API, see the table below, looking at the Save Stage and SUO/Target API columns where the Target is SUO controls and containers)
PFC6 comes with several objects with the full SUO API built in. These objects are:
- all windows (descendants of pfc_w_master: including w_child, w_frame, w_main, w_popup, w_response, w_sheet)
- DataWindows (u_dw)
- datastores (n_ds)
- tabs (u_tab)
- custom visual objects and tabpages (descendants of pfc_u_base: including u_tabpg)
- listviews (u_lv, u_lvs)
- treeviews (u_tv, u_tvs)
- DataWindow updating services (n_cst_dwsrv_Linkage)
However, one is not restricted to these objects. Any powerobject that has all or part (but always at least the Update stage) of the SUO API implemented can be included in a logical unit of work. An example would be a SingleLineEdit that updates a field in the INI file. The update stage may be a call to SetProfileString ().
How does the Logical Unit of Work Service define its List of Objects
The LUW service defines a set of objects that it will work against by default. The process usually starts at the window level. The process goes through the control list, querying each object using metaclass functionality to discover which objects implement the self-updating object API. If it finds a SUO, it will add that object, plus it will query the object for its update list. The list of objects that the LUW service operates against can always be programmatically define using of_SetUpdateObjects ().
The SUO API
For an object you create yourself to be self-updating, it must comply with the SUO API if it is to work with the Logical Unit of Work service. For each stage, the object may or may not implement the corresponding function. However, the function for the Update stage is always required. The functions related to each of the object-related stages are:
|
Stage:
|
AcceptText
|
|
Prototype:
|
of_AcceptText (ab_FocusOnError) returns integer
|
|
Description:
|
This stage is analogous to its DataWindow cousin. It should provide simple data validation such as data type checking, range checking, or checking against simple rules. Data should not be validated against data from other objects, as the simple validation of the other objects may not yet have been performed. Returns -1 for failures, 1 for successes.
|
|
Stage:
|
UpdatesPending
|
|
Prototype:
|
of_UpdatesPending () returns integer
|
|
Description:
|
Returns the number of objects with updates pending if there are updates to be done in this object; returns 0 if there are none.
|
|
Stage:
|
Validation
|
|
Prototype:
|
of_Validation () returns integer
|
|
Description:
|
An additional error checking step. This step may contain cross-object validation. Returns -1 for failures, 1 for successes.
|
|
Stage:
|
UpdatePrep
|
|
Prototype:
|
of_UpdatePrep () returns integer
|
|
Description:
|
Returns -1 for failures, 1 for successes.
|
|
Stage:
|
Update
|
|
Prototype:
|
of_Update (ab_AcceptText, ab_ResetFlag, lpo_UpdateRequestor) returns integer
|
|
Description:
|
Required!! This step is analogous to its DataWindow cousin also. Returns -1 for failures, 1 for successes.
|
|
Stage:
|
PostUpdate
|
|
Prototype:
|
of_PostUpdate () returns integer
|
|
Description:
|
Returns -1 for failures, 1 for successes.
|
Save Process Overview
|
Save Stage:
|
The stage of the save process as viewed by the Logical Unit of Work service
|
|
Target:
|
The object (or object type) from the list of objects in the logical unit of work.
|
|
SUO/Target API:
|
The function call(s) against the target by the Logical Unit of Work service
|
|
Application Level Logic Location:
|
(SUOs only) The place defined by the standard PFC SUOs to implement application specific code for the given stage
|
|
Default Functionality:
|
The behaviour that the standard PFC DataWindows and datastores implement by default in the Application Level Logic Location
|
|
Save Stage
|
Target
|
SUO/Target API
|
Application Level Logic Location
|
Default Functionality for DataWindows and DataStores
|
|
AcceptText
|
All SUO controls and containers
|
of_AcceptText (ab_FocusOnError)
|
Event pfc_AcceptText
|
AcceptText()
|
|
All other containers
|
of_AcceptText ( luo_Control.Control, ab_FocusOnError )
|
|
|
|
All other DataWindows and datastores
|
If ldw_nonpfc.RowCount() + ldw_nonpfc.FilteredCount() + ldw_nonpfc.ModifiedCount() + ldw_nonpfc.DeletedCount() > 0 Then
la_rc = ldw_NonPFC.AcceptText()
|
|
|
|
UpdatesPending
|
All SUO controls and containers
|
of_UpdatesPending ()
|
DWs & DSs: Event pfc_UpdatesPending
Windows: Event pfc_UpdatesPendingRef
|
IF of_SetUpdateable ()=TRUE AND ((ModifiedCount()+DeletedCount()) > 0)
|
|
All other containers
|
of_UpdatesPending ( lw_control.control )
|
|
|
|
All other DataWindows and datastores
|
ls_Updatetable = ldw_nonpfc.Describe("DataWindow.Table.UpdateTable")
If ls_updatetable = '?' or ls_updatetable = '' Then
lb_updatespending = False
Else
lb_updatespending = (ldw_nonpfc.ModifiedCount() + ldw_nonpfc.DeletedCount() >= 1)
End If
|
|
|
|
Validation
|
All SUO controls and containers with pending updates or all SUO controls and containers
|
of_Validation ()
|
Event pfc_Validation
|
FindRequired ()
|
|
All other [pending] SUO containers
|
of_validation (lw_control.control)
|
|
|
|
All other [pending] DataWindows and datastores
|
ue_validation ()
|
|
|
|
Exit the process if no updates pending
|
|
UpdatePrep
|
Pending SUO controls and containers
|
of_UpdatePrep ()
|
Event pfc_UpdatePrep
|
None
|
|
Other pending containers
|
of_UpdatePrep ( lw_control.control )
|
|
|
|
Other pending DataWindows and datastores
|
ue_updateprep ()
|
|
|
|
PreUpdate
|
|
|
Local to LUW service
|
|
|
BeginTran
|
Transaction or transaction array
|
atr_control[li_i].of_Begin()
|
Local to LUW service
|
|
|
Update
|
Pending SUO controls and containers
|
of_Update (ab_AcceptText, ab_ResetFlag, lpo_UpdateRequestor)
|
Event pfc_Update
|
Update (TRUE, FALSE)
|
|
Other pending containers
|
of_Update ( lw_control.control, ab_accepttext, ab_resetflag )
|
|
|
|
Other pending DataWindows and datastores
|
Update (ab_accepttext, ab_resetflag)
|
|
|
|
EndTran
|
Transaction or transaction array
|
If ai_saverc > 0 Then
If atr_control[li_i].of_Commit() < 0 Then...
Else
If atr_control[li_i].of_Rollback() < 0 Then...
|
Local to LUW service
|
|
|
If an error occurred during the save, report the DBError and exit
|
|
PostUpdate
|
Pending SUO controls and containers
|
of_PostUpdate ()
|
Event pfc_PostUpdate
|
ResetUpdate()
|
|
Other pending containers
|
of_PostUpdate ( lw_control.control )
|
|
|
|
Other pending DataWindows and datastores
|
ResetUpdate()
|
|
|
1. Shows default functionality for DataWindows and datastores. Containers cascade the API calls in all cases by default. Descriptions exclude functionality conditional on the
Linkage or Multitable Services
2. Unless a filter has been defined through of_SetTypeToProcess (string as_ObjectType) (e.g. of_SetTypeToProcess (DataWindow))
3. Windows, tabs and custom visual user objects. (Note: tabpages are viewed as custom visual user objects to PowerBuilder internally.)
4. Dependent on of_SetAlwaysValidate(boolean), which toggles all controls validated whether updates pending or not
LUW Service Developers Guide
Example task 1
Tabpage with DataWindows. No datastores. Order of update unimportant. No save time validations.
Tabpage Constructor
// Instantiate the LUW service
of_SetLogicalUnitOfWork (TRUE)
Example task 2
Tabpage with an update that requires DataWindow updates, datastore updates and embedded SQL. Save time validations are also required.
Tabpage Constructor
window lw_Parent
// Create datastore with either:
ids_ResponseContents = CREATE n_ds // do not CREATE datastore
ids_ResponseContents.DataObject = d_ResponseContents
// OR
ids_ResponseContents = CREATE ds_ResponseContents // where ds_ResponseContents is a descendent of n_ds
// Instantiate the LUW service
of_SetLogicalUnitOfWork (TRUE)
// Define the order of update and/or include objects other than DataWindows
of_SetUpdateObjects ([dw_UpdateMeFirst, dw_Second, ids_ResponseContents])
// Associate datastores with a window (required for some LUW functions)
of_GetParentWindow (lw_Parent)
ids_ResponseContents.of_SetParentWindow (lw_Parent)
Tabpage pfc_Update (ab_AcceptText, ab_ResetFlag) Event
override ancestor event
integer li_Return
// Allow ancestor event to process objects in update array and capture return value / success indicator
li_Return = Super::Event pfc_Update (ab_AcceptText, ab_ResetFlag)
// If ancestor was successful, run embedded SQL
IF li_Return = 1 THEN
UPDATE ... ;
IF SQLCA.of_SQLError () <> 0 THEN
li_Return = -1
...
END IF
END IF
RETURN li_Return
Save time validations
Save time validations are coded in:
- DataWindow controls pfc_Validation events
- datastore objects pfc_Validation events
- tabpage pfc_Validation events (e.g. cross DataWindow validation)
Example task 3
Tabpage with two DataWindows. One is for display purposes only. The other should be updated to the database at save time.
Tabpage Constructor
// Instantiate the LUW service
of_SetLogicalUnitOfWork (TRUE)
dw_DisplayOnly Constructor
// Flag DataWindow to be excluded from LUW save process
of_SetUpdateable (FALSE)
This document is also available as an Acrobat file.
|