Emissaries exist to help clients issue perfectly formatted business operation requests. Creating emissaries involves:
Emissaries are responsible for helping Windows DNA application clients issue perfectly formatted requests for executants to perform various business operations. This responsibility includes providing clients with any supplemental information they need from the application to formulate valid business operation requests. To identify such supplemental data, developers must analyze each business operation request. For example, the DNA PurchaseOrder application requires clients issuing purchase order creation requests to provide several pieces of information, including the order's destination shipping address and the inventory ID of each item they wish to purchase as part of the order. While the user doesn't require any supplemental information from the application to supply the order's destination shipping address, the user does need the application to provide the inventory IDs of the various items the user wants to purchase. The DNA PurchaseOrder application provides users with this information by presenting a list that includes each inventory item's ID along with its description, cost, and current quantity on hand.
Figure 14. Inventory selection
Emissaries are responsible for helping Windows DNA application clients issue perfectly formatted requests for executants to perform various business operations, which includes providing clients with any supplemental information they need from the application to formulate valid business operation requests. The Windows DNA PurchaseOrder application requires clients issuing purchase order creation requests to provide several pieces of information, including the order's destination shipping address and the inventory ID of each item that they wish to purchase as part of the order.
The DNA PurchaseOrder application provides special iterator objects to assist emissaries in providing clients with the supplemental information they need to issue business operation requests. The DNA PurchaseOrder application's iterator objects are designed to behave like ADO recordsets and even provide similar properties and methods.
Table 17. DNA PurchaseOrder Application Iterator Object Properties and Methods
Property/method | Data type | Description |
(P) AbsolutePage | Long | Specifies which page to move for a new current record. |
(P) AbsolutePosition | Long | Specifies the ordinal position of the internal recordset's current record. |
(P) BOF | Boolean | Indicates whether the current record position is before the first record in the internal recordset. |
(P) Bookmark | Variant | Specifies a bookmark that uniquely identifies a record in the internal recordset. |
(P) CacheSize | Long | Specifies the number of records that are cached locally in memory. |
(M) Clone | Recordset | Creates a duplicate recordset. |
(P) EOF | Boolean | Indicates that the current record position is after the last record in the internal recordset. |
(P) Filter | String | Indicates the selection criteria for selectively screening out records. |
(M) Initialize | Boolean | Initializes the iterator object by instructing it to issue a request to obtain an ADO disconnected recordset for internal usage. |
(P) Item | Object | Returns an emissary that represents the record identified by the current record position. |
(P) MaxRecords | Long | Indicates the maximum number of records to return from a query. |
(M) Move | N/A | Moves the current record position a user-specified number of records forward or backward. |
(M) MoveFirst | N/A | Moves the current record position to the first record in the internal recordset. |
(M) MoveLast | N/A | Moves the current record position to the last record in the internal recordset. |
(M) MoveNext | N/A | Moves the current record position one record forward, toward the bottom of the internal recordset. |
(M) MovePrevious | N/A | Moves the current record position one record backward, toward the top of the internal recordset. |
(P) PageCount | Long | Indicates how many pages of data the internal recordset contains. |
(P) PageSize | Long | Indicates how many records constitute one page in the internal recordset. |
(P) RecordCount | Long | Indicates the current number of records in the internal recordset. |
(P) Sort | String | Sorts the records in the internal recordset. |
(P) State | Long | Indicates the current state of the internal recordset. |
However, unlike an ADO recordset, an iterator is designed to return an emissary that represents the record identified by the current record position. The emissary returned by an iterator can be used like any other emissary to prepare and issue requests for executants to perform various business operations. Before a client can use an iterator, the iterator must be initialized. Clients initialize iterators by providing the selection criteria that describes the records they're interested in. The iterator uses the user-provided selection criteria to formulate and issue a request to the appropriate executant, which responds by returning an ADO disconnected recordset to the iterator. The iterator caches the disconnected recordset and uses it to perform the various operations defined by the iterator. The following code snippet illustrates how the DNA PurchaseOrder application uses an inventory iterator to display the list of inventory items available for purchase:
Private Sub DisplayInventory()
Dim objInventory As Inventory
Dim objListViewItemAdded As ListItem
' Clear any existing contents
lvInventory.ListItems.Clear
Set mobjInventoryIterator = New InventoryIterator
mobjInventoryIterator.Initialize
' Sort the items
mobjInventoryIterator.Sort = "Description ASC"
' Display the Inventory Items
Do While Not mobjInventoryIterator.EOF
Set objInventory = mobjInventoryIterator.Item
Set objListViewItemAdded = lvInventory.ListItems.Add(, "Key=" & CStr(objInventory.ID), CStr(objInventory.ID))
' Description
objListViewItemAdded.SubItems(1) = objInventory.Description
' Price
objListViewItemAdded.SubItems(2) = Format$(objInventory.Price, "Currency")
' Quantity on Hand
objListViewItemAdded.SubItems(3) = CStr(objInventory.QOH)
mobjInventoryIterator.MoveNext
Loop
' Enable the Done button if necessary
cmdDone.Enabled = Not (lvInventory.SelectedItem Is Nothing)
' Clean Up
Set objListViewItemAdded = Nothing
Set objInventory = Nothing
End Sub
Because the DNA PurchaseOrder application's iterators are built on top of ADO disconnected recordsets, they are capable of being downloaded and executed on the client's machine.
The DNA PurchaseOrder application relies on four different iterators to provide clients with the supplemental data they need to issue properly formatted business operation requests: the AccountIterator, the InventoryIterator, the LineItemIterator, and the PurchaseOrderIterator, each of which is designed to return a specific type of emissary.
To create the iterators used by the DNA PurchaseOrder application
To minimize the network traffic associated with issuing invalid business operation requests, emissaries should validate each client request as thoroughly as possible before actually issuing the request to an executant. For example, in order to create a new customer account, the DNA PurchaseOrder application requires the customer to provide their five-digit United States Postal Service (U.S.P.S) ZIP code. Unless the Account emissary has access to the entire list of valid U.S.P.S. ZIP codes, there is no way for the emissary to completely validate whether the ZIP code supplied by the customer is valid. However, the Account emissary can surely validate whether the ZIP code supplied by the customer is exactly five numeric characters.
Private Property Let IAccount_Zip(ByVal RHS As String)
Dim intCharacterCode As Integer
Dim intPos As Integer
Dim blnInvalid As Boolean
' Perform any data validation that does not require
' access to the data services
' Zip must be 5 numeric characters
If Len(RHS) = 5 Then
blnInvalid = False
For intPos = 1 To 5
intCharacterCode = Asc(Mid$(RHS, intPos, 1))
If intCharacterCode < 48 Or intCharacterCode > 57 Then
blnInvalid = True
Exit For
End If
Next intPos
' If the new value is valid then save it
If Not blnInvalid Then
mstrZip = RHS
End If
Else
blnInvalid = True
End If
' Track whether or not the property is invalid
mobjInvalidProperties.Track IDS_INVALID_ACCOUNT_ZIP, blnInvalid
' Notify the client of any error
If blnInvalid Then
Err.Raise vbObjectError + IDS_INVALID_ACCOUNT_ZIP, LoadResString(IDS_PROJECTNAME), LoadResString(IDS_INVALID_ACCOUNT_ZIP)
End If
End Property
So, while emissaries may not always be able to completely validate each client request, they should always attempt to validate client requests as thoroughly as possible in order to minimize the unnecessary network traffic associated with issuing invalid requests.
When validating client data, emissaries should perform property-level validation as well as object-level validation. Property-level validation is the process of ensuring an individual property is within a predefined range of acceptable values. Supporting property-level validation allows emissaries to provide immediate feedback to interactive clients (for example, clients with dynamic HTML or Win32-based presentation services).
Figure 15. Supporting property-level validation allows emissaries to provide immediate feedback to interactive clients like clients that rely on dynamic HTML or Win32-based presentation services.
Object-level validation is the process of ensuring all of an object's properties are within their respective predefined ranges of acceptable values. Supporting object-level validation allows emissaries to provide feedback to non-interactive clients (for example, clients with HTML 3.2-based presentation services supported via Active Server Pages (ASP) or Internet Server API (ISAPI) filters or extensions).
Figure 16. Supporting object-level validation allows emissaries to provide feedback to non-interactive clients like clients that rely on HTML 3.2-based presentation services supported via Active Server Pages (ASP) or Internet Server API (ISAPI) filters or extensions.
Figure 17. Supporting object-level validation allows emissaries to provide feedback to non-interactive clients like clients that rely on HTML 3.2-based presentation services supported via Active Server Pages (ASP) or Internet Server API (ISAPI) filters or extensions.
Each DNA PurchaseOrder emissary supports both property-level and object-level validation. To provide object-level validity, each DNA PurchaseOrder emissary relies on a special InvalidProperties helper object to track each invalid property individually:
' Track whether or not the property is invalid
mobjInvalidProperties.Track INVALID_ACCOUNT_ZIP, blnInvalid
By tracking invalid properties, the InvalidProperties object makes it easy for the emissary to provide object-level validation:
Private Property Get IAccount_Valid() As Boolean
' The entire object cannot be valid unless
' all of its properties are valid
IAccount_Valid = (mobjInvalidProperties.Count = 0)
End Property
To implement the DNA PurchaseOrder emissaries and related support objects
Table 18. "Invalid Property" Error Messages
Value | Description |
101 | DNA Purchase Order. |
102 | The account must have a first name. |
103 | The account must have a last name. |
104 | The account must have an address. |
105 | The account must have a city. |
106 | The account must have a two-character state abbreviation. |
107 | The account must have a five-digit ZIP code. |
108 | The account could not be destroyed. Make sure that the account exists, and that you have the proper authorization to delete an account. |
109 | The account could not be saved. Make sure that you have the proper authorization to save an account. |
110 | The inventory item must have a description. |
111 | The inventory item cannot have a negative price. |
112 | The inventory item cannot have a negative quantity on hand. |
113 | The inventory item could not be destroyed. Make sure that the item exists, and that you have the proper authorization to delete an inventory item. |
114 | The inventory item could not be saved. Make sure that you have the proper authorization to save an inventory item. |
115 | The line item must have a valid inventory item. |
116 | The line item cannot have a negative price. |
117 | The line item cannot have a negative quantity. |
118 | The line item could not be destroyed. Make sure that the item exists, and that you have the proper authorization to delete a line item. |
119 | The line item could not be saved. Make sure that you have the proper authorization to save a line item. |
120 | The purchase order must have a valid account number. |
121 | The purchase order cannot have a negative shipping charge. |
122 | The purchase order must have a "ShipToFirstName." |
123 | The purchase order must have a "ShipToLastName." |
124 | The purchase order must have a "ShipToAddress." |
125 | The purchase order must have a "ShipToCity." |
126 | The purchase order must have a two-character "ShipToState" abbreviation. |
127 | The purchase order must have a five-digit numeric "ShipToZip" code. |
128 | The purchase order cannot have a negative tax rate. |
129 | The purchase order cannot have a negative total. |
130 | The purchase order could not be cancelled. Make sure that you have the proper authorization to cancel a purchase order. |
131 | The purchase order could not be destroyed. Make sure that the purchase order exists, and that you have the proper authorization to delete a purchase order. |
132 | The purchase order could not be saved. Make sure that you have the proper authorization to save a purchase order. |
Once all of the emissaries and support objects have been completely implemented, save your work, and compile the ActiveX DLL into POEmissaries.dll.