In most cases, the ID is the corresponding database ID, but what about when a worker object is created based on a table with a multiple field primary key? In this case, the ID would return a concatenated string of these fields, even though they would exist as explicit properties in their own right.
Displaying the data!
Here is the internal worker code for the ID property-the property responsible for setting and returning the worker object ID. Note that this code is identical for every other Property Let/Get pair in the worker object.
Public Property Get ID() As Long PiSetAbsoluteRowPosition ID = oPicRecordset("ID") End Property Public Property Let ID(i_ID As Long) PiSetAbsoluteRowPosition PiSetPropertyValue "ID", i_ID End Property
The most important point here is the PiSetAbsoluteRowPosition call. This call is required to point the worker object to the correct row in the shared recordset. The recordset current record at this point is undefined-it could be anywhere. The call to PiSetAbsoluteRowPosition is required to make sure that the worker object is retrieving the correct row from the recordset.
Private Sub PiSetAbsoluteRowPosition() oPiRecordset.AbsolutePosition = lPiRowIndex End Sub
Likewise, this call to PiSetAbsoluteRowPosition needs to happen in the Property Let. The PiSetPropertyValue procedure merely edits the appropriate row in the recordset.
Private Sub PiSetPropertyValue(i_sFieldName As String, _ i_vFieldValue As Variant) oPiRecordset.Edit oPiRecordset(i_sFieldName) = i_vFieldValue oPiRecordset.Update End Sub
Methods in the madness
At the moment, all we've concentrated on are the properties of worker objects. What about methods?
An Employee worker object might have methods such as AssignNewProject. How do you implement these methods? Well, there are no special requirements here-implement the customer business methods as you see fit. Just remember that the data is in the shared recordset and that you should call PiSetAbsoluteRowPosition before you reference any internal data.
Worker objects creating factories
Factory objects returning workers is all well and good, but what happens when we want to create relationships between our business objects? For example, an Employee object might be related to the Roles object, which is the current assignment this employee has. In this case, the Employee worker object will return a reference to the Roles factory object. The Employee object will be responsible for creating the factory object and will supply any parameters required for its instantiation. This is great because it means we need only to supply parameters to the first factory object we create. Subsequent instantiations are managed by the worker objects themselves.
Dim ocDAL As New cDAL Dim ocfEmployees As New cfEmployees Dim ocwEmployee As New cwEmployee Dim ocParams As New cParams ocfEmployees.Create ocDAL ocParams.Add "ID", "637" ocfEmployees.Populate ocParams MsgBox ocfEmployees(1).Roles.Count
Here the Roles property on the Employee worker object returns the ocfRoles factory object.
Public Property Get Roles() As cfRoles Dim ocfRoles As New cfRoles Dim ocParams As New cParams ocParams.Add "EmpID", Me.ID ocfRoles.Create oPicDAL ocfRoles.Populate ocParams Set Roles = ocfRoles End Property
Accessing child factory objects this way is termed navigated instantiation, and you should bear in mind this important performance consideration. If I wanted to loop through each Employee and display the individual Roles for each Employee, one data access would retrieve all the employees via the DAL and another data access would retrieve each set of Roles per employee. If I had 1800 employees, there would be 1801 data access operations-one operation for the Employees and 1800 operations to obtain the Roles for each employee. This performance would be suboptimal.
In this case, it would be better to perform direct instantiation, which means you'd create the Roles for all employees in one call and then manually match the Roles to the appropriate employee. The Roles object would return the EmployeeID, which we would then use to key into the Employee factory object to obtain information about the Employee for this particular Roles object. The golden rule here is that navigated instantiation works well when the number of data access operations will be minimal; if you need performance, direct instantiation is the preferred method.
Dim ocfRoles As New cfRoles Dim ocfEmployees As New cfEmployees Dim ocwRole As cwRole ocfEmployees.Create oPicDAL ocfRoles.Create oPicDAL ocfEmployees.Populate ' Using default populate to retrieve all objects ocfRoles.Populate For Each ocwRole In ocfRoles MsgBox ocfEmployees(ocwRole.EmpID).Name Next ' ocwRole
An interesting scenario occurs when a worker object has two different properties that return the same type of factory object. For example, a worker could have a CurrentRoles property and a PreviousRoles property. The difference is that these properties supply different parameters to the underlying factory object Populate procedure.
Query a worker object to determine what factory objects it supports as children
It's useful to be able to query a worker object to determine what factory objects it supports as children. Therefore, a worker object contains the read-only property Factories, which enables the code that dynamically determines the child factory objects of a worker and can automatically instantiate them. This is useful for utilities that manipulate business objects.
The Factories property returns a cParams object containing the names of the properties that return factories and the names of those factory objects that they return. Visual Basic can then use the CallByName function to directly instantiate the factories of children objects, if required.
The Factories property is hidden on the interface of worker objects because it does not form part of the business interface; rather, it's normally used by utility programs to aid in dynamically navigating the object model.
Dim ocfEmployees As New cfEmployees Dim ocfEmployee As New cfEmployee Dim ocParams As New cParams ocfEmployees.Create oPicDAL ocfEmployees.Populate Set ocfEmployee = ocfEmployees(1) Set ocParams = ocfEmployee.Factories