Maintaining Customer Records in the Island Hopper News Sample

Julie MacAller
Microsoft Corporation

January 1999

Introduction

The Island Hopper News sample is an automated classified ads system created by a fictitious company, Island Hopper Publishing, as a test project to evaluate converting the current paper-based weekly newspaper into an online newspaper. The design team consists of the Island Hopper News editor, the Classified Ads and Accounting department heads, and two developers.

This paper describes how the code for maintaining customer records in Island Hopper fits together and how it works.

Maintaining Customer Records Overview

Maintaining customer records is a common scenario for any application that has customer records stored in a database. Generally, maintaining customer records involves adding, deleting, and updating records in the database. The Island Hopper News application is an example of performing these functions using a Microsoft® Windows-based client, middle-tier business and data access objects, and a SQL Server database. Because Island Hopper News is a distributed application, it showcases the use of Microsoft® Transaction Server (MTS) and Microsoft® ActiveX Data Objects (ADO) as technologies that make it possible to perform customer maintenance operations in a distributed application.

Customer Maintenance Components

Adding, deleting, and updating customer records use the following Island Hopper business, data access, and utility components:

Function Business Components Data Access Components Utility Components
Adding a new customer record bus_CustomerC db_CustomerC

db_Customer
PasswordC

util_TakeANumber

util_TakeANumber
Update

Deleting a customer record bus_CustomerC db_CustomerC

db_Customer
PasswordC

 
Updating a customer record bus_CustomerC db_CustomerC

db_Customer
PasswordC


Each of these components implements the following methods:

Component Methods
bus_Customer Add, GetByEmail, IbusCustomerChange_Add, IbusCustomerChange_Delete, IbusCustomerChange_Update, IbusCustomerLookup_GetByEmail, IbusCustomerLookup_GetByID, IbusCustomerLookup_ListByLastName, Validate
db_Customer IcustomerChange_Add, IcustomerChange_Delete, IcustomerChange_Update, ICustomerLookup_GetByEmail, ICustomerLookup_GetByID, ICustomerLookup_ListByLastName, UpdateBalance
db_CustomerPassword IcustomerPasswordChange_Add, IcustomerPasswordChange_Delete, IcustomerPasswordChange_Update, ICustomerPasswordLookup_GetByID
db_Product Add, Delete, GetByID, GetUnitPrice, ListByDesc, Update
util_TakeANumber GetANumber
util_TakeANumberUpdate Update

The utility components, util_TakeANumber and util_TakeANumberUpdate, assign blocks of unique IDs to customers, ads, payments, and invoices, and maintain the next available ID number for each group of items in the TakeANumber table. For information on assigning blocks of unique IDs this way, see the white paper Assigning Blocks of Unique IDs in the Island Hopper News Sample.

Generally, the bus_CustomerC component creates instances of the data access components it needs, depending on which database table is being accessed. If bus_CustomerC needs to query or update the Customers table, it creates an instance of db_CustomerC; if it needs to query or update the CustomerPasswords table, it creates an instance of db_CustomerPasswordC. The following figure shows the relationship between business components, data access components, and database tables.

Customer Maintenance User Interface

In the Island Hopper news application, maintaining customer records is a function performed by Island Hopper Publishing employees using the Windows client. To use the Customer Maintenance function, an employee clicks Customer Maintenance on the main form, which displays the Customer Maintenance form, frmCustomerC (shown in the following figure).

The Customer Maintenance form presents the employee with three options:

Employees choose an option by clicking its associated option button. Clicking the option button results in minor changes to the Customer Maintenance form. (For example, buttons might be disabled or the labels on buttons might change.)

Both the update and delete operations require the employee to retrieve a specific customer record from the database first. Employees can supply the customer ID or e-mail address to retrieve a customer, or they can search the database by last name. Searching the database by last name generates a list of customers with the same or similar last names; employees can then select the customer they want. The customer information is retrieved from the database and placed into the form fields.

To update a customer, employees can edit the customer information as necessary and then click the Save button to update the database.

To delete a customer, employees click the Delete button. A confirmation message displays. If employees click Yes on the message box, the customer record is deleted from the database.

To add a new customer record, employees fill out the customer information fields and click Save. First the code tries to retrieve a customer that matches the supplied information; this step ensures duplicate records aren't created. If the retrieval fails, then the code inserts the new customer record into the database. When the record is added, a confirmation message displays that shows the new customer ID number.

Customer Maintenance Process

From a developer's point of view, there are five parts to the customer maintenance process:

Each of these parts is discussed in this white paper.

Retrieving a Customer Record

All customer maintenance operations require the retrieval of a customer record from the database. Even the Add operation attempts to retrieve a customer record containing the customer information in the form to make sure it doesn't already exist. Although retrieving by customer ID and retrieving by e-mail address are nearly identical operations, retrieving by last name is a little different, so it is described separately.

Retrieving by ID or e-mail address

In this step, the employee supplies either a customer ID or an e-mail address, and the appropriate procedure (cmdRetrieveID_Click or cmdRetrieveEmail_Click) retrieves the corresponding customer record from the database. The code for both the cmdRetrieveID_Click and the cmdRetrieveEmail_Click procedures is the same.

The retrieval process consists of the following steps:

The following figure illustrates the process of getting customer information from the database.

  1. The cmdRetrieveID_Click or cmdRetrieveEmail_Click procedure accesses the IbusCustomerLookup interface on the bus_CustomerC component, which was created during the frmCustomerC Form_Load procedure.
    Set iCustomerLookup = objCustomer
    

    The iCustomerLookup variable is declared as type IBUSCUSTOMERLib.IbusCustomerLookup at the top of frmCustomerC.

    Private icustomerLookup As IBUSCUSTOMERLib.IbusCustomerLookup
    

    In the Form_Load procedure, the CreateObject function is called to create an instance of the bus_CustomerC component and assign the instance to the objCustomer variable.

    Set objCustomer = CreateObject("bus_CustomerC.Customer")
    

    Then, the Set statement is used to access the IbusCustomerLookup interface on the bus_CustomerC object.

    Note   Another way to accomplish this would be to initialize a variable for the IbusCustomerLookup interface in the Form_Load procedure and use that variable in each method.

  2. The cmdRetrieveID_Click or cmdRetrieveEmail_Click procedure calls the appropriate retrieval method (GetByID or GetByEmail) on the IbusCustomerLookup interface to the bus_CustomerC object, passing in the customer ID or e-mail address.

  3. The GetByID or GetByEmail method of the bus_CustomerC object calls the MTS CreateInstance method on the MTS ObjectContext object to create the data access component, db_CustomerC.

    The db_CustomerC component is marked as "requires a transaction." Creating the object using the MTS CreateInstance method starts a new transaction.

  4. The GetByID or GetbyEmail method of the bus_CustomerC object calls the appropriate retrieval method (GetByID or GetByEmail) on the db_Customer object, passing in the customer ID or e-mail address.

  5. The GetByID or GetByEmail method of the db_CustomerC object creates a new ADO Recordset object and calls the global RunSp function to run a stored procedure, passing the name of the stored procedure and any other parameters.

    For more information about RunSp and the use of stored procedures, see "Retrieving Data From the Database" in the Island Hopper Code Organization white paper.

  6. The stored procedure finishes running and passes a recordset with the requested information back to the GetByID or GetByEmail method of the db_CustomerC object.

  7. The GetByID or GetByEmail method of the db_CustomerC object calls the MTS SetComplete method on the MTS ObjectContext object to release any held resources and to signal approval of—but not commit—the transaction.

  8. The GetByID or GetByEmail method of the db_CustomerC object returns the recordset to the GetByID or GetByEmail method of the bus_CustomerC object.

The following steps summarize the process of getting the customer password information from the database.

  1. The GetByID or GetByEmail method of the bus_CustomerC object calls the MTS CreateInstance method on the MTS ObjectContext object to create the db_CustomerPasswordC component.

    The db_CustomerPasswordC component is marked as "requires a transaction," so it enlists in the current transaction. The CreateInstance method of the ObjectContext object causes the transaction to flow from one object to another. If you just used the CreateObject method, the transaction would not flow.

  2. The GetByID or GetByEmail method of the bus_CustomerC object calls the GetByID method of the db_CustomerPasswordC object, passing in the customer ID.

  3. The GetByID method of the db_CustomerPasswordC object creates a SQL SELECT command to retrieve a record from the CustomerPasswords table where the customer ID matches the supplied customer ID.

  4. The GetByID method of the db_CustomerPasswordC object sets the recordset CursorLocation property to adUseclient, which is required for disconnected recordsets.

    A disconnected recordset is a recordset in a client cache that no longer has a live connection to the server. If you need to do something with the original data source, such as updating data, you need to re-establish the connection. Using disconnected recordsets minimizes use of database connections, which helps make an application more scalable.

  5. The GetByID method of the db_CustomerPasswordC object executes the SQL command to the database using the Recordset.Open method, passing in the following information:

    Note   In your own applications, you will probably want to use user or system DSNs or specify the connect information in a connect string to avoid the performance penalty of using a file DSN.

  6. The GetByID method of the db_CustomerPasswordC object calls the MTS SetComplete method on the MTS ObjectContext object to release any held resources and to signal approval of the transaction, and then it returns the customer password information to the GetByID or GetByEmail method of the bus_CustomerC object.

  7. The GetByID or GetByEmail method of the bus_CustomerC object calls the MTS SetComplete method on the MTS ObjectContext object to release any held resources and to signal approval of the transaction, and then it returns the customer and password information to frmCustomerC.cmdRetrieveID_Click or frmCustomerC.cmdRetrieveEmail_Click.

The following steps summarize the process of populating the form fields with the customer and password information.

  1. The frmCustomerC.cmdRetrieveID_Click or frmCustomerC.cmdRetrieveEmail_Click procedure checks to see whether the recordset containing the customer information is empty.

  2. If the recordset is empty, the retrieval method displays a message that no customer record was found and calls the ClearFields method to clear the form fields and prepare the form for the next operation.

  3. If the recordset contains at least one record, the retrieval method calls the FillCustomerInfo method to fill the fields on the form with information from the recordset and then extracts the customer password from the recordset containing customer password information and puts it into the Password field on the form.

  4. The retrieval method enables the Save command button and calls the DisableModes method to disable the mode option buttons until the employee presses Cancel.

Retrieving by last name

In this step, the employee clicks the Search By Last Name button (frmCustomerC.cmdSearch), which displays the Customer Search form (frmCustomerSearch). On the Customer Search form, the employee supplies a last name and then clicks the Search button (frmCustomerSearch.cmdSearch), and the cmdSearch_Click procedure retrieves all records that match that last name. The employee then selects the customer record of interest, and the code retrieves the customer and password information from the database.

The retrieval process consists of the following steps:

Retrieval for the selected customer proceeds as described in the preceding section, "Retrieving by ID or E-mail Address."

The following steps summarize the process of getting the list of customers that match the supplied last name.

  1. The employee clicks the Search By Last Name button on the Customer Maintenance form (frmCustomerC.cmdSearch), and the frmCustomerC.cmdSearch_Click procedure extracts any text from the Last Name field on frmCustomerC, puts it in the Last Name field on frmCustomerSearchC, and displays frmCustomerSearchC.

  2. The employee types in the last name, if it isn't already present, and clicks the Search button (frmCustomerSearchC.cmdSearch), which calls the cmdSearch_Click procedure.

  3. The cmdSearch_Click procedure on frmCustomerSearchC calls the ListByLastName method on the IbusCustomerLookup interface of the bus_CustomerC object, passing in the last name.

  4. The ListByLastName method of the bus_CustomerC object calls the MTS CreateInstance method on the ObjectContext object to create an instance of the data access component, db_CustomerC.

  5. The ListByLastName method of the bus_CustomerC object calls the ListByLastName method on the ICustomerLookup interface of the db_CustomerC object, passing in the last name.

  6. The ListByLastName method of the db_CustomerC object calls the global RunSp function to run the Customer_ListByLastName stored procedure.

  7. When the stored procedure finishes running, it returns a recordset containing the list of customers whose last names match the supplied last name to the ListByLastName method of the db_CustomerC object.

  8. The ListByLastName method of the db_CustomerC object calls the MTS SetComplete method on the MTS ObjectContext object to release any held resources and to signal approval of the transaction, and then it returns the list of customers to the ListByLastName method of the bus_CustomerC object.

  9. The ListByLastName method of the bus_CustomerC object calls the MTS SetComplete method on the MTS ObjectContext object to release any held resources and to signal approval of the transaction, and then it returns the list of last names to the cmdSearch_Click procedure on frmCustomerSearchC.

The following steps summarize the process of populating the customer list on frmCustomerSearchC.

  1. The cmdSearch_Click procedure on frmCustomerSearchC checks the recordset containing the list of last names to see whether it is empty.

  2. If the recordset is empty, the cmdSearch_Click procedure on frmCustomerSearchC displays a message that no customer was found.

  3. If the recordset contains at least one record, the cmdSearch_Click procedure on frmCustomerSearchC loops through the recordset to fill the lstCustomers listbox control with name and e-mail address for each retrieved customer and saves the customer ID numbers in an array for easy retrieval when the employee selects a customer record to work with.

Employees can now click the customer they want and then click the Retrieve button (cmdRetrieve) or double-click the customer they want, which has the same effect. Clicking cmdRetrieve calls cmdRetrieve_Click, which in turn calls lstCustomers_DblClick; double-clicking a customer calls lstCustomers_DblClick. After employees select the customer they want, it's necessary to pass the customer ID for that customer back to frmCustomerC.

The following steps summarize the process of passing the customer ID back to frmCustomerC.

  1. The lstCustomers_DblClick procedure calls the AcceptCustomerID procedure on frmCustomerC, which takes the customer ID from the array created by frmCustomerSearch, converts it to data type String, and places it into the Customer ID field on frmCustomerC.

  2. The AcceptCustomerID procedure on frmCustomerC calls the cmdRetrieveID_Click procedure on frmCustomerC to retrieve the rest of the customer information and the password.

Validating Information

Both the Update and Add operations require validating changed or new customer information before updating the database. The Island Hopper News sample performs simple validation on the client and validation that requires a database lookup operation on the server. For example, when entering a classified ad, the validation code on the client ensures data values are present, but the validation code on the server ensures the ad has 200 or fewer words. If you were going to validate that a zip code and state matched, for example, you would do that on the server, rather than downloading a huge validation table to every client machine.

The client validation code for updating or adding customers is quite simple. It resides in the ValidateFields procedure in frmCustomerC, which is called by both the UpdateCustomer and AddCustomer methods. All this procedure does is make sure each field on the form contains a value.

Updating Customer Records

To update customer records, employees click the Update option button on the Customer Maintenance form and then choose a way to retrieve the customer record they want to update. Employees can retrieve a customer record by customer ID, e-mail address, or last name. For information on how retrieval works, see "Retrieving a Customer Record" in this white paper.

After the customer record is retrieved from the database, the data is sent back to the client, and the fields on the Customer Maintenance form are filled with the information from the customer record. Employees can change any field except for the customer ID and the balance. When they finish making changes, they click the Save button (cmdSave) to update the record in the database.

The following steps summarize the update process.

  1. The cmdSave_Click procedure in frmCustomerC calls the UpdateCustomer procedure, also in frmCustomerC.

  2. The UpdateCustomer procedure in frmCustomerC calls the ValidateFields procedure, also in frmCustomerC, to validate the information in the fields on the client. For information on how validation works, see "Validating Information" in this white paper.

  3. The UpdateCustomer procedure accesses the IbusCustomerChange interface on the business component, bus_CustomerC, and calls the Update method on the IbusCustomerChange interface to the bus_CustomerC object.

  4. The Update method of the bus_CustomerC object calls the MTS CreateInstance method on the MTS ObjectContext object to create an instance of the data access component, db_CustomerC.

    The db_CustomerC component is marked as "requires a transaction," so it enlists in the current transaction.

  5. The Update method of the bus_CustomerC object calls the Update method of the db_CustomerC object.

  6. The Update method of the db_CustomerC object builds a SQL UPDATE command and uses the Command.Execute method to execute the command to the database.

  7. When the command finishes executing, the Update method of the db_CustomerC object calls the MTS SetComplete method on the MTS ObjectContext object to release any held resources and to signal approval of the transaction and then passes control back to the Update method of the bus_CustomerC object.

  8. The Update method of the bus_CustomerC object calls the MTS CreateInstance method on the MTS ObjectContext object to create an instance of the data access component, db_CustomerPasswordC.

    The db_CustomerPasswordC component is marked as "requires a transaction," so it enlists in the current transaction.

  9. The Update method of the bus_CustomerC object calls the Update method of the db_CustomerPasswordC object.

  10. The Update method of the db_CustomerPasswordC object builds a SQL UPDATE command and uses the Command.Execute method to execute the command to the database.

  11. When the command finishes executing, the Update method of the db_CustomerPasswordC object calls the MTS SetComplete method on the MTS ObjectContext object to release any held resources and to signal approval of the transaction, and then it passes control back to the Update method of the bus_CustomerC object.

  12. The Update method of the bus_CustomerC object calls the MTS SetComplete method on the MTS ObjectContext object again to complete the transaction and pass control back to frmCustomerC.UpdateCustomer.

  13. The UpdateCustomer procedure in frmCustomerC displays a confirmation message that the customer record was updated successfully.

Deleting Customer Records

To delete customer records, employees click the Delete option button on the Customer Maintenance form and then choose a way to retrieve the customer record they want to delete. Employees can retrieve a customer record by customer ID, e-mail address, or last name. For information on how retrieval works, see "Retrieving a Customer Record" in this white paper.

After the customer record is retrieved from the database, the data is sent back to the client and the fields on the Customer Maintenance form are filled with the information from the customer record. Employees click the Delete button (cmdSave) to delete the record. A confirmation message displays; employees click Yes to confirm the deletion or No to cancel it.

The following steps summarize the deletion process.

  1. The cmdSave_Click procedure in frmCustomerC calls the DeleteCustomer procedure, also in frmCustomerC.

  2. The DeleteCustomer procedure displays a message asking if the employee wants to delete the customer record.

    If the employee clicks No, focus returns to the Customer Maintenance form, where the employee can click the Cancel button to clear the form.

  3. If the employee clicks Yes, the DeleteCustomer procedure accesses the IbusCustomerChange interface on the business component, bus_CustomerC, and calls the Delete method on the IbusCustomerChange interface to the bus_CustomerC object.

  4. The Delete method of the bus_CustomerC object calls the MTS CreateInstance method on the MTS ObjectContext object to create an instance of the data access component, db_CustomerPasswordC.

    The db_CustomerPasswordC component is marked as "requires a transaction," so it enlists in the current transaction.

  5. The Delete method of the bus_CustomerC object calls the Delete method of the db_CustomerPassword object.

  6. The Delete method of the db_CustomerPasswordC object builds a SQL DELETE command and uses the Command.Execute method to execute the command to the database. This deletes the customer password from the CustomerPasswords table in the database.

  7. When the command finishes executing, the Delete method of the db_CustomerPasswordC object calls the MTS SetComplete method on the MTS ObjectContext object to release any held resources and to signal approval of the transaction, and then it passes control back to the Delete method of the bus_CustomerC object.

  8. The Delete method of the bus_CustomerC object calls the MTS CreateInstance method on the MTS ObjectContext object to create an instance of the data access component, db_CustomerC.

    The db_CustomerC component is marked as "requires a transaction," so it enlists in the current transaction.

  9. The Delete method of the bus_CustomerC object calls the Delete method of the db_CustomerC object.

  10. The Delete method of the db_CustomerC object builds a SQL DELETE command and uses the Command.Execute method to execute the command to the database. This deletes the customer record from the Customers table in the database.

  11. When the command finishes executing, the Delete method of the db_CustomerC object calls the MTS SetComplete method on the MTS ObjectContext object to release any held resources and to signal approval of the transaction, and then it passes control back to the Delete method of the bus_CustomerC object.

  12. The Delete method of the bus_CustomerC object calls the MTS SetComplete method on the MTS ObjectContext object once again to complete the transaction and pass control back to the frmCustomerC.DeleteCustomer.

  13. The DeleteCustomer procedure of frmCustomerC displays a confirmation message that the customer record was deleted successfully.

Adding Customer Records

To add a new customer record, employees click the Add option button on the Customer Maintenance form, fill in all the customer information fields, and click Save (cmdSave).

Before the record is added to the database, the application tries to retrieve it using the e-mail address. This retrieval attempt ensures the record doesn't already exist in the database. For information on how retrieval works, see "Retrieving Customer Information" in this white paper.

The following steps summarize the add process.

  1. The cmdSave_Click procedure in frmCustomerC calls the AddCustomer procedure, also in frmCustomerC.

  2. The AddCustomer procedure in frmCustomerC calls the ValidateFields procedure, also in frmCustomerC, to validate the information in the fields on the client. For information on how validation works, see "Validating Information" in this white paper.

  3. The AddCustomer procedure accesses the IbusCustomerChange interface on the business component, bus_CustomerC, and calls the GetByEmail method on the IbusCustomerChange interface to the bus_CustomerC object. This is an attempt to retrieve the customer record from the database using the e-mail address as the search criteria. For information on how retrieval works, see "Retrieving a Customer Record" in this white paper.

    If the recordset the retrieval process returns contains a match for the e-mail address, the AddCustomer procedure displays a message and exits.

  4. If the recordset the retrieval process returns is empty, the AddCustomer procedure accesses the IbusCustomerChange interface on the business component, bus_CustomerC, and calls the Add method on the IbusCustomerChange interface to the bus_CustomerC object.

  5. The Add method of the bus_CustomerC object calls the MTS CreateInstance method on the MTS ObjectContext object to create an instance of the utility component, util_TakeANumber.

    The util_TakeANumber component is marked as "requires a transaction," so it enlists in the current transaction.

  6. The Add method of the bus_CustomerC object calls the GetANumber method of the util_TakeANumber object to generate a unique customer ID. For information on the TakeANumber component and how it assigns unique IDs, see the Island Hopper white paper, Assigning Blocks of Unique IDs in Island Hopper.

  7. After the util_TakeANumber object returns the unique customer ID to the Add method of the bus_CustomerC object, the Add method calls the MTS CreateInstance method on the MTS ObjectContext object to create an instance of the data access component, db_Customer.

    The db_CustomerC component is marked as "requires a transaction," so it enlists in the current transaction.

  8. The Add method of the bus_CustomerC object calls the Add method of the db_CustomerC object.

  9. The Add method of the db_CustomerC object builds a SQL INSERT statement and uses the Command.Execute method to add the customer record to the database.

  10. When the command finishes executing, the Add method of the db_CustomerC object calls the MTS SetComplete method on the MTS ObjectContext object to release any held resources and to signal approval of the transaction, and then it passes control back to the Add method of the bus_CustomerC object.

  11. The Add method of the bus_CustomerC object calls the MTS CreateInstance method on the MTS ObjectContext object to create an instance of the data access component, db_CustomerPasswordC.

    The db_CustomerPasswordC component is marked as "requires a transaction," so it enlists in the current transaction.

  12. The Add method of the bus_CustomerC object calls the Add method of the db_CustomerPasswordC object.

  13. The Add method of the db_CustomerPasswordC object builds a SQL INSERT statement and uses the Command.Execute method to add the customer password record to the database.

  14. When the command finishes executing, the Add method of the db_CustomerPasswordC object calls the MTS SetComplete method on the MTS ObjectContext object to release any held resources and to signal approval of the transaction, and then it passes control back to the Add method of the bus_CustomerC object.

  15. The Add method of the bus_CustomerC object calls the MTS SetComplete method on the MTS ObjectContext object once again and returns the new customer ID number to frmCustomerC.AddCustomer.

  16. The AddCustomer procedure in frmCustomerC displays a confirmation message that the record was added successfully.

For More Information