Placing Ads in the Island Hopper News Sample

Julie MacAller
Microsoft Corporation

January 1999

Introduction

The Island Hopper News application 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 the placing ads function in the Island Hopper News application fits together and how it works.

Placing Ads Overview

If you have ever submitted a classified ad to your local newspaper or other publication, you understand what the Island Hopper Publishing developers needed to do to translate this function to software:

Island Hopper Publishing customers place classified ads by accessing the Island Hopper News Web site, logging in, and entering their ad on a submission form. If they are new users, they must create an account for themselves before they can place ads.

Placing Ads Components

Placing ads uses the bus_AdC and bus_CustomerC business components, the db_AdC, db_CategoryC, db_CustomerC, db_CustomerPasswordC, and db_ProductC data access components, and the util_TakeANumber and util_TakeANumberUpdate utility components.

These components expose the following methods:

Component Methods
bus_AdC Delete, GetByID, ListByCategory, ListByCustEmail, ListByCustID, PlaceAd, UpdateAd
bus_CustomerC Add, GetByEmail, IbusCustomerChange_Add, IbusCustomerChange_Delete, IbusCustomerChange_Update, IbusCustomerLookup_GetByEmail, IbusCustomerLookup_GetByID, IbusCustomerLookup_ListByLastName, Validate
db_AdC Add, Delete, GetAdCountByCategory, GetByID, ListByCategory, ListByCustEmail, ListByCustID, Update
db_CategoryC Add, Delete, GetByID, GetByName, GetBySearchString, ListAll, ListByRange, Update
db_CustomerC IcustomerChange_Add, IcustomerChange_Delete, IcustomerChange_Update, ICustomerLookup_GetByEmail, ICustomerLookup_GetByID, ICustomerLookup_ListByLastName, UpdateBalance
db_CustomerPasswordC IcustomerPasswordChange_Add, IcustomerPasswordChange_Delete, IcustomerPasswordChange_Update, ICustomerPasswordLookup_GetByID
db_ProductC Add, Delete, GetByID, GetUnitPrice, ListByDesc, Update
util_TakeANumber GetANumber
util_TakeANumberUpdate Update

Placing Ads User Interface

To place classified ads, a customer points to the Island Hopper Web site using an Internet browser. After the Web site loads, the customer clicks the Classified Ads link. This link goes to the Browse/Place an Ad page (browse.asp), where the customer can choose whether to browse or place classified ads. If customers want to place classified ads, they must log in by entering their e-mail address and password and then clicking the Go link. This action loads another page, which verifies their e-mail address and password and then displays the ad entry form, AdEntry.asp.

If customers do not have an account yet, they click a link called New User. This link loads the Customer Maintenance page, where customers can establish an account for themselves by providing their name, e-mail address, mailing address, telephone number, and a password. They click Submit to submit their information to the Island Hopper News database. If the database ad operation succeeds, customers see a confirmation message. Then they are routed to a login page, where they enter their e-mail address and password to access the ad entry form.

When they reach the ad entry form, customers enter information about the ad they want to place. When they are finished, they click a link called Preview, which displays the ad as it will appear, the ad duration, and the cost of the ad. If customers are satisfied with their ad, they click a link called Submit to submit their ad to Island Hopper News. If the ad submission process succeeds, customers see a confirmation message. They can then choose to either enter another ad or browse existing ads.

Placing Ads Process

From a developer's point of view, there are three main parts to the placing ads process:

Each of these parts is discussed in this white paper.

Preparing the Ad Entry Form

After a user logs in and clicks the Go link, the browser requests the AdEntry.asp page from the server. The AdEntry.asp page contains both the ad entry form and the preview pane, where the customer's ad is displayed as it will appear.

The AdEntry.asp page uses the @TRANSACTION=REQUIRED processing directive to specify a transaction. A transaction is a server operation that succeeds or fails as a whole, even if the operation involves many steps, such as ordering, checking inventory, and billing. Transaction processing is used to update databases reliably. When you are making many related changes to a database or updating several databases at once, you want to ensure all of the changes are correctly executed. If any of the changes fail, you want to restore the original state of the database tables.

Making the AdEntry.asp page transactional means Microsoft® Transaction Server (MTS) will manage the transaction. All of the Island Hopper middle-tier components are marked as "requires a transaction," which means they will enlist in the current transaction. If no transaction is current, MTS will start a new transaction. The AdEntry.asp page uses several of the Island Hopper middle-tier objects; marking the page as transactional ensures all those components will participate in the same transaction.

Preparing the ad entry form consists of the following steps:

  1. Verifying the user's e-mail address and password.

  2. Retrieving the unit prices for ads of one- and two-week durations.

  3. Building the list of ad categories for the Category drop-down list on the ad entry form.

Verify the user's e-mail address and password

This step ensures the customer already has an account with Island Hopper Publishing. Only customers with accounts can submit classified ads. Customer information is retrieved from the classified ads database using the customer's e-mail address and password. If a customer has an account, his or her information is retrieved from the classified ads database and placed into the ad entry form automatically. If a customer does not have an account, he or she is redirected to the Login.asp page to create one.

The processing in this step is carried out by the VerifyUserByEmail procedure on the AdEntry.asp page. The following steps summarize the process.

  1. The VerifyUserByEmail procedure creates an instance of the business component, bus_CustomerC.

    The bus_CustomerC component is marked as "requires a transaction," so the object instance enlists in the current transaction.

  2. The VerifyUserByEmail procedure checks for the customer's e-mail address, and sends the customer to the login page, login.asp, if the e-mail address is missing.

  3. If the VerifyUserByEmail function finds an e-mail address for the user, it calls the Validate method on the bus_CustomerC object to validate the password and e-mail address.

  4. The Validate method of the bus_CustomerC object creates an instance of the data access component, db_CustomerC, and then it calls the GetByEmail method of the db_CustomerC object to retrieve the customer information from the database using the e-mail address.

    The db_CustomerC component is marked as "requires a transaction," so it enlists in the current transaction, which started when the server started processing AdEntry.asp.

    The object is created using the CreateInstance method of the MTS GetObjectContext object. This ensures the db_CustomerC object enlists in the proper transaction.

  5. The GetByEmail method of db_CustomerC calls a global function, RunSp, to run a stored procedure called Customer_GetByEmail. RunSp creates an updateable, disconnected recordset.

    A disconnected recordset is a recordset in a client cache that no longer has a live connection to the server. What this means for Island Hopper News is it's possible to connect to the database, retrieve some records, return the records to the client, and disconnect from the database. The client can use the records as needed and then connect to the database again, if necessary, to make any updates.

    For more information on RunSp, see the section on "Stored Procedures" in the Island Hopper Code Organization white paper.

  6. The GetByEmail method of the db_CustomerC object signals approval of the transaction if no errors occur and returns the recordset containing the customer information to the Validate method of the bus_CustomerC object.

    Signalling approval of the transaction does not commit the transaction yet. Several other objects are still enlisted in, and participating in, the current transaction. The transaction isn't committed until all participating objects approve.

  7. The Validate method of the bus_CustomerC object now creates an instance of the data access component db_CustomerPasswordC and calls the GetByID method of the db_CustomerPasswordC object, passing in the CustomerID field from the recordset returned by the GetByEmail method of the db_CustomerC object.

    The db_CustomerPasswordC component is marked as "requires a transaction," so it enlists in the current transaction, which started when the server started processing AdEntry.asp.

    Notice that the object is created using the CreateInstance method of the MTS GetObjectContext object. This ensures db_CustomerPasswordC enlists in the proper transaction.

  8. The GetByID method of the db_CustomerPasswordC object creates a Microsoft® ActiveX® Data Objects (ADO) recordset object and then builds a SQL command to get the password from the database, given the customer ID.

  9. The GetByID method of the db_CustomerPasswordC object sets the CursorLocation property to adUseClient—required for disconnected recordsets—and executes the SQL command to the database using the Recordset Open method.

    Calling the Open method on the Recordset object is recommended when you want data returned in a recordset, because you can specify both a cursor type and a lock type. In addition, the Open method uses the ActiveConnection property to pass the current Connection object.

  10. If no errors occur, 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—but not commit—the transaction. Then the GetByID method of the db_CustomerPasswordC object returns the recordset containing the password to the Validate method of the bus_CustomerC object.

  11. The Validate method of the bus_CustomerC object compares the retrieved password with the password the customer supplied on AdEntry.asp and displays an error message if the passwords don't match.

  12. If the passwords do match, the Validate 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—but not commit—the transaction. Then the Validate method of the bus_CustomerC object returns the recordset containing the customer information to AdEntry.asp.

  13. AdEntry.asp checks for errors and sends the user to the Login.asp page if it finds any.

  14. If no errors occurred, AdEntry.asp calls the FillOutForm function to insert values from the recordset containing the customer information into the module-level variables on the page.

    When the FillOutForm function finishes, control returns to AdEntry.asp, which calls the AdUnitPrice function.

Retrieve ad unit prices

This step retrieves the unit prices for classified ads. Classified ads are considered products that Island Hopper Publishing offers. As such, they have product codes. Island Hopper Publishing offers two kinds of classified ads: those that run for one week and those that run for two weeks. Each of these is a different product with a different product code. The ad prices are retrieved during setup of the ad entry form, so they're available later when the ad preview is shown. The ad preview includes the price of the ad.

The processing in this step is carried out by the AdUnitPrice procedure on the AdEntry.asp page. The following steps summarize the process.

  1. The AdUnitPrice function creates an instance of the data access component, db_ProductC.

    The db_ProductC component is marked as "requires a transaction," so it enlists in the current transaction, which started when the server started processing AdEntry.asp.

  2. The AdUnitPrice function calls the GetUnitPrice method on the db_ProductC object, passing in a product code of AD-100, which represents a 100-word classified ad.

  3. The GetUnitPrice method of the db_ProductC object builds a SQL statement to extract records from the Products table in the database where the Product Code field matches the passed-in product code.

  4. The GetUnitPrice method of the db_ProductC object then creates an ADO Connection object to connect to the database and opens the Connection using a file DSN that identifies the database.

  5. After opening the database connection, the GetUnitPrice method of the db_ProductC object creates an ADO Recordset object and uses the Execute method on the Connection object to execute the SQL command it built previously.

  6. When the command finishes executing, the GetUnitPrice method of the db_ProductC 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. Then the GetUnitPrice method of the db_ProductC object returns the unit price for product code AD-100 to the AdUnitPrice function of AdEntry.asp.

  7. The AdUnitPrice function stores the unit price for product code AD-100 in the first position of the m_UnitPrice array (a module-level variable used throughout the AdEntry.asp page), and then it calls the GetUnitPrice method of the db_CustomerC object again, this time passing in a product code of AD-200.

    When the GetUnitPrice method of the db_ProductC object returns the unit price for product code AD-200 to the AdUnitPrice function, AdUnitPrice returns control to AdEntry.asp.

Building the list of ad categories

This step retrieves the list of categories for classified ads. Classified ads are required to have a category assigned to them. The ad entry form includes a drop-down list that shows all the categories, so customers can choose the one that best fits their classified ad. Getting the list from the database ensures it is always up to date.

The processing in this step is carried out by the ListCategories procedure on the AdEntry.asp page. The following steps summarize the process.

  1. The ListCategories function creates an instance of the data access component, db_CategoryC, and calls the ListAll method of the db_CategoryC object to retrieve the list of ad categories from the database and store them in a recordset.

    The db_CategoryC component is marked as "requires a transaction," so it enlists in the current transaction, which started when the server started processing AdEntry.asp.

  2. The ListAll method of the db_CategoryC object builds a SQL statement to extract a list of categories from the database and order them alphabetically by category name.

  3. After building the SQL statement, the ListAll method of the db_CategoryC object creates an ADO recordset object and sets the CursorLocation property on the recordset to adUseClient, which is required for disconnected recordsets.

  4. The ListAll method of the db_CategoryC object executes the SQL command to the database using the Open method on the recordset.

  5. When the command finishes executing, the ListAll method of the db_CategoryC 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. Then the ListAll method of the db_CategoryC object returns the list of categories to the ListCategories function of AdEntry.asp.

  6. The ListCategories function of AdEntry.asp checks for errors and displays a message to the user if it finds any.

  7. The ListCategories function then loops through the recordset containing the list of categories to populate the list of categories on the form.

  8. When the ListCategories function finishes, AdEntry.asp finishes building the HTTP response, commits the transaction, and sends the page to the customer's browser. Now the customer can enter the classified ad on the ad entry form and click Preview to see what the ad will look like.

Validating and Previewing

Customers fill out the ad entry form with the category, ad title, body text, start date, and duration of their classified ad and then click Preview to see what the ad will look like online. Clicking Preview does not request a different page from the server; instead, a combination of client-side scripts and DHTML change the AdEntry.asp page to a preview pane. Processing the preview pane consists of the following steps:

  1. Validate the ad information

  2. Set up the ad preview pane

  3. Calculate the cost of the ad

  4. Show the ad preview pane

Validate the ad information

This step validates the information the customer has entered in the ad entry form. All validation occurs on the client. If any field on the ad entry form generates an error, the customer is notified with an error message. Customers must fix the errors before they can click Preview again.

The processing in this step is carried out by the switchUI, checkData, and checkDate functions on the AdEntry.asp page. The switchUI function is triggered by clicking the Preview link and calls the checkData and checkDate functions. The following steps summarize the process.

  1. The switchUI function starts by checking to see whether the ad entry form is visible and then calls the checkData function to validate the data entered in the ad entry form.

    The checkData function validates the information in each field on the ad entry form. In most cases, the validation simply ensures the field is not blank. In the ad body, the checkData function ensures no HTML characters are within the ad body. This check makes it possible to show the preview of the ad exactly as it appears; extraneous HTML characters could affect the display of the ad.

  2. For the Start Date field, the checkData function calls the checkDate function to check both the format of the date and the value of the date. The start date must be in the format month, day, year (mm/dd/yy), and the value cannot be earlier than tomorrow's date.

    The checkDate function returns control to the checkData function, which finishes validating the rest of the form fields. If no errors occur, the checkData function returns control to the switchUI function.

Set up the ad preview pane

This step sets up the ad preview pane. The ad preview pane shows the customer's classified ad, formatted exactly as it will appear once it is accepted and starts running. In addition, the preview pane includes the customer's billing information, the duration of the ad, and the price of the ad. If customers approve of how the ad appears and confirm the other information, they can click Submit on the preview pane to submit the ad to the database. If customers want to change something, they can click Change on the preview pane to return to the ad entry form.

The processing in this step is carried out by the switchUI and setPreview functions on the AdEntry.asp page. The switchUI function calls the setPreview function. The setPreview function takes the information from the ad entry form, field by field, and shows it on the preview pane, formatted exactly as it will appear.

Calculate the cost of the ad

This step calculates the cost of the customer's ad and returns the price to the setPreview function, so it can be displayed on the preview pane. Classified ads of one-week duration are priced differently from classified ads of two-week duration. This step gets the ad duration value from the ad entry form and assigns the correct price to the ad from the m_UnitPrice array.

The processing in this step is carried out by the setPreview and getAdPrice functions on the AdEntry.asp page. The setPreview function calls the getAdPrice function. The getAdPrice function returns the correct price of the ad to the setPreview function, which then writes the ad price onto the preview pane and returns control to the switchUI function.

Show the preview pane

This step hides the ad entry form and shows the preview pane. The switchUI function is responsible for both of these actions.

Submitting the Ad

Customers submit their ad by clicking the Submit link on the Preview pane. A Results page is displayed (AdConfirm.asp), which displays a confirmation message and the new ad ID number if the submission was successful. Processing for this stage consists of the following steps:

  1. Check for input from the previous page

  2. Validate ad information

  3. Update current user data with data from the previous page

  4. Insert the classified ad into the database

Processing starts when the user clicks the Submit link, which calls the Submit method on the document, triggers the onsubmit event, and requests the AdConfirm.asp page.

AdConfirm.asp starts by setting the scripting language to Microsoft® Visual Basic® Scripting Edition (VBScript), starting a new transaction, and executing the Option Explicit command to force the declaration of all variables.

Check input from previous page

This step verifies that the customer accessed this page from the AdEntry.asp page and redirects the customer to the login page if necessary.

The processing in this step is carried out by the checkUserPath function on the AdConfirm.asp page. The checkUserPath function checks to see whether the e-mail address on the ad entry form is complete. If it is empty, the script redirects the user to the Login.asp page and returns control to the AdConfirm.asp page.

Validate ad information

This step validates the ad information to ensure it can be inserted into the database without causing SQL Server errors. When the Set Quoted_Identifier property is on (ADO sets this property on by default), SQL Server will not accept strings enclosed in double quotation marks. Strings enclosed in single quotation marks (') are OK, but strings cannot have any other single quotation marks inside them if they will be enclosed in single quotation marks.

The processing in this step is carried out by the InsertAd, GetValuesOnQuery, and SQLSafe procedures on the AdConfirm.asp page. The following steps summarize the process.

  1. The AdConfirm.asp page calls the InsertAd procedure.

  2. The InsertAd procedure creates instances of the bus_AdC and bus_CustomerC components.

    Both bus_AdC and bus_CustomerC are marked as "requires a transaction," so they enlist in the current transaction, which started when AdConfirm started processing.

  3. The InsertAd procedure calls the GetValuesOnQuery function, which gets the information entered on AdEntry.asp, runs each piece of information through the SQLSafe function to make sure the information can be entered into the database, and assigns the values to the module-level variables at the top of AdConfirm.asp.

    SQLSafe uses the VBScript function Trim to remove leading and trailing spaces from a passed-in value. It then calls the VBScript function CStr to convert the value to a Variant of subtype String. Finally, SQLSafe calls the VBScript function Replace to replace individual single quotes in a string with doubled single quotes. If there are any errors, SQLSafe displays a message; otherwise, it returns a safe version of the value to the GetValuesOnQuery function.

  4. The GetValuesOnQuery function calculates the end date of the ad based on the start date and the duration and then converts the result to a string.

  5. When the GetValuesOnQuery function finishes with the ad information, it returns control to the InsertAd procedure.

Update current user data

This step compares the customer information entered on the ad entry form with the customer information in the database and updates the database where necessary. Customers can update their address, city, state, postal code, country, or phone number this way.

The processing in this step is carried out by the InsertAd and CheckField procedures on the AdConfirm.asp page. The following steps summarize the process.

  1. The InsertAd function calls the CStr function to make sure the customer's e-mail address is in the correct format and then creates an ADO recordset on the server called rs_Customer.

  2. Then InsertAd calls the GetByEmail method of the bus_CustomerC object to retrieve the customer information, passing in the customer's e-mail address.

  3. The GetByEmail method of the bus_CustomerC object gets the IbusCustomerLookup interface on the bus_CustomerC object (instead of the default interface) and calls the GetByEmail method of that object.

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

  4. The GetByEmail method of the bus_CustomerC object creates an instance of the data access component, db_CustomerC, and calls the GetByEmail method on that object.

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

  5. The GetByEmail method of the db_CustomerC object calls the global function RunSp to execute the Customer_GetByEmail stored procedure in the database.

  6. The Customer_GetByEmail stored procedure extracts the customer information and returns it to the GetByEmail method of the db_CustomerC object in a recordset.

  7. The 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, and then it returns the recordset to the GetByEmail method of the bus_CustomerC object.

  8. The GetByEmail method of the bus_CustomerC object checks to make sure the password is not missing and the recordset containing the customer information is not empty, and then it creates an instance of the data access component, db_CustomerPasswordC, and calls the GetByID method on the db_CustomerPasswordC object.

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

  9. The GetByID method of the db_CustomerPasswordC object creates an ADO recordset object and then builds a SQL command to get the password from the database, given the customer ID.

  10. The GetByID method of the db_CustomerPasswordC object sets the CursorLocation property to adUseClient—required for disconnected recordsets—and executes the SQL command to the database using the Recordset Open method.

  11. If no errors occur, 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—but not commit—the transaction. Then the GetByID method of the db_CustomerPasswordC object returns the recordset containing the password to the GetByEmail method of the bus_CustomerC object.

  12. The GetByEmail method of the bus_CustomerC object also calls the MTS SetComplete method on the MTS ObjectContext object and passes the recordset back to the InsertAd function of AdConfirm.asp.

  13. The InsertAd function checks for errors in retrieving the information. If any errors occurred, InsertAd displays an error message and calls the MTS SetAbort method of the MTS ObjectContext object to cancel the transaction.

  14. If the customer information was retrieved from the database without errors, the InsertAd function checks for errors in the start date format. If any errors exist, InsertAd displays an error message and calls the MTS SetAbort method of the MTS ObjectContext object to cancel the transaction.

  15. If there are no errors in the date format, the InsertAd function gets the customer ID from the recordset and then calls the CheckField function to update the customer address, city, state, postal code, country, and phone number information.

    The CheckField function compares the value of a field in the recordset, such as address, with the value in the address field on the form and updates the fields in the recordset with the values from the fields on the form.

  16. The InsertAd function calls the CheckField function for each field that might need updating and then checks whether errors occurred during updating. If errors occurred, the InsertAd function displays an error message and calls the MTS SetAbort method of the MTS ObjectContext object to cancel the transaction.

    If no errors occurred, the InsertAd function calls the PlaceAd method on the bus_AdC object. The PlaceAd method does the work of inserting the ad and all its associated information into the database.

Insert the ad into the database

This step inserts the ad and all its associated information into the database. Inserting the ad is a multi-step process that includes the following steps:

  1. Retrieve the ID of the category

  2. Get a unique ID for the new ad

  3. Count the number of words in the ad and raise an error if the count is too high

  4. Calculate the price for the ad

  5. Create a new invoice for this ad and this customer

  6. Update the database with the new ad

If the insertion process succeeds, a confirmation message that includes the unique ad ID is returned to the customer.

This step gets the ID for the ad category. This ID is passed as a parameter later in the PlaceAd method when the ad is actually inserted into the database. Retrieving the ID includes the following steps.

  1. The PlaceAd method of the bus_AdC object starts by creating an instance of the data access component, db_CategoryC, and calling the GetByID method of the db_CategoryC object to retrieve the ID for the ad category the customer specified in the ad entry form.

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

  2. The GetByID method of the db_CategoryC object creates an ADO Connection object to connect to the database and opens the Connection using a file DSN that identifies the database.

  3. The GetByID method of the db_CategoryC object builds a SQL command to extract records from the Categories table in the database where the category ID matches the passed-in category ID.

  4. The GetByID method of the db_CategoryC object creates an ADO Recordset object and uses the Execute method on the Connection object to execute the SQL command.

  5. When the command finishes executing, the GetByID method of the db_CategoryC 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. Then the GetByID method of the db_CategoryC object returns a recordset containing the list of categories to the PlaceAd method of the bus_AdC object.

This step gets a unique ID for the new classified ad. This ID is passed as a parameter later in the PlaceAd method of the bus_AdC object when the ad is actually inserted into the database. One of the Island Hopper News business rules is that each ad must have a unique ID. Obtaining that unique ID is part of business processing, not part of data access. That's why the ad ID is obtained now and passed to the db_AdC object, which does the work of inserting the ad into the database. Getting the ID includes the following steps.

  1. The PlaceAd method of the bus_AdC object creates an instance of the utility component, TakeANumber, and calls the GetANumber method on the object.

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

  2. The GetANumber method of the TakeANumber object gets the next available unique ID from the database.

    The GetANumber method queries the database for the next available number for a classified ad. For more information on how this procedure works—and why it's used at all—see the Assigning Blocks of Unique Ids in the Island Hopper News Sample white paper.

  3. The GetANumber method of the TakeANumber 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, and then it returns the new ad ID number to the PlaceAd method of the bus_AdC object.

This step counts the number of words in the ad. Ads of more than 200 words are not accepted. Determining the word count of the ad includes the following steps.

  1. The PlaceAd method of the bus_AdC object creates an instance of the utility component, WordCount, and calls the CountWords method on the WordCount object.

    The WordCount component is implemented using Microsoft® Visual C++® to take advantage of the string-handling capabilities of the C language. The component is marked as "requires a transaction," so it enlists in the current transaction.

  2. The CountWords method of the WordCount object takes the ad body as input, counts the number of words in the ad body, and returns the count to the PlaceAd method of the bus_AdC object.

  3. The PlaceAd method of the bus_AdC object evaluates the word count. If the word count is greater than 200, the PlaceAd method displays an error message.

This step calculates the price of the ad based on the ad duration. Ads that run for one week cost $15.00; ads that run for two weeks cost $17.50. Calculating the price for the ad includes the following steps.

  1. The PlaceAd method of the bus_AdC object creates an instance of the data access component, db_ProductC.

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

  2. The PlaceAd method of the bus_AdC object calculates the ad duration by subtracting the ad start date from the ad end date, and adding one. If the result is greater than seven, the ad duration is two weeks (product code AD-200); otherwise, the ad duration is one week (product code AD-100).

  3. The PlaceAd method of the bus_AdC object calls the GetUnitPrice method of the db_ProductC object to determine the price of the ad.

  4. The GetUnitPrice method of the db_ProductC object builds a SQL statement to extract records from the Products table in the database where the Product Code field matches the passed-in product code.

  5. The GetUnitPrice method of the db_ProductC object then creates an ADO Connection object to connect to the database and opens the Connection using a file DSN that identifies the database.

  6. After opening the database connection, the GetUnitPrice method of the db_ProductC object creates an ADO recordset object and uses the Execute method on the Connection object to execute the SQL command it built previously.

  7. When the command finishes executing, the GetUnitPrice method of the db_ProductC 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. Then the GetUnitPrice method returns the ad price to the PlaceAd method of the bus_AdC object.

This step creates a new billing invoice for this ad and customer. Creating a new invoice includes the following steps.

  1. The PlaceAd method of the bus_AdC object creates an instance of the business component, bus_InvoiceC. The bus_InvoiceC component is marked as "requires a transaction," so it enlists in the current transaction.

  2. The PlaceAd method of the bus_AdC object then calls the AddHeader method of the bus_InvoiceC object. Invoices consist of a header that contains the invoice ID, customer ID, e-mail address, last name, and first name, and a detail section that shows the invoice ID, invoice date, ad ID, and description.

  3. The AddHeader method of the bus_InvoiceC object creates an instance of the utility component, TakeANumber, and calls the GetANumber method of the object.

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

  4. The GetANumber method of the TakeANumber object queries the database for the next available ID for invoices and then returns that number to the AddHeader method of the bus_InvoiceC object.

  5. The AddHeader method of the bus_InvoiceC object creates an instance of the data access component, db_InvoiceC, and calls the AddHeader method of the db_InvoiceC object.

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

  6. The AddHeader method of the db_InvoiceC object builds a SQL command to insert the new invoice into the Invoices table in the database.

  7. The AddHeader method of the db_InvoiceC object then creates an ADO Command object and sets the CommandText property of the object to the SQL command it just built.

  8. The AddHeader method of the db_InvoiceC object makes a series of calls to the Append method of the Parameters collection for the Command object, using the CreateParameter method on the Command object. This technique defines the parameters the SQL INSERT command requires and improves performance by eliminating the need to query the database for the parameter requirements before building them.

  9. The AddHeader method of the db_InvoiceC object creates a new ADO Connection object and executes the Open method to connect to the database.

  10. The AddHeader method of the db_InvoiceC object sets the ActiveConnection property of the ADO command object to the currently open connection and calls the Execute method of the Command object to execute the command to the database.

  11. When the command finishes executing, the AddHeader method of the db_InvoiceC 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. Then the AddHeader method returns the invoice ID to the AddHeader method of the bus_InvoiceC object, which returns the invoice ID to the PlaceAd method of the bus_AdC object.

  12. The PlaceAd method of the bus_AdC object calls the AddDetail method on the bus_InvoiceC object.

  13. The AddDetail method of the bus_InvoiceC object creates an instance of the db_InvoiceC object and calls the AddDetail method.

  14. The AddDetail method of the db_InvoiceC object builds a SQL statement to insert an invoice detail record into the database.

  15. The AddDetail method of the db_InvoiceC object creates a new ADO Command object and sets the CommandText property to the SQL command it just built.

  16. The AddDetail method of the db_InvoiceC object then makes a series of calls to the Append method of the Parameters collection for the Command object, using the CreateParameter method on the Command object. This technique defines the parameters the SQL INSERT command requires and improves performance by eliminating the need to query the database for the parameter requirements before building them.

  17. The AddDetail method of the db_InvoiceC object creates a new ADO Connection object and executes the Open method to connect to the database.

  18. The AddDetail method of the db_InvoiceC object sets the ActiveConnection property of the ADO command object to the currently open connection and calls the Execute method of the Command object to execute the command to the database.

  19. When the command finishes executing, the AddDetail method of the db_InvoiceC 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. Then the AddDetail method of the db_InvoiceC object passes control to the AddDetail method of the bus_InvoiceC object.

  20. The AddDetail method of the bus_InvoiceC object calls the MTS EnableCommit method on the MTS ObjectContext object to release any held resources and to signal approval of—but not commit—the transaction. Then the AddDetail method of the bus_InvoiceC object passes control to the PlaceAd method of bus_AdC.

This step updates the database with the new classified ad. Adding the new classified ad to the database includes the following steps.

  1. The PlaceAd method of the bus_AdC object creates an instance of the data access component, db_AdC, and calls the Add method on the db_AdC object.

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

  2. The Add method of the db_AdC object builds the SQL INSERT statement for adding the classified ad to the database and then creates a new ADO Command object and sets the CommandText property of the object to the SQL command.

  3. Next, the Add method of the db_AdC object makes a series of calls to the Append method of the Parameters collection for the Command object, using the CreateParameter method on the Command object. This technique defines the parameters the SQL INSERT command requires and improves performance by eliminating the need to query the database for the parameter requirements before building them.

  4. Finally, the Add method of the db_AdC object creates a new ADO Connection object, opens the Connection object, and executes the command to the database.

  5. When the command finishes executing, the Add method of the db_AdC 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. Then the Add method of the db_AdC object passes control to the PlaceAd method of the bus_AdC object.

  6. The PlaceAd method of the bus_AdC object also calls the MTS SetComplete method and then passes control back to the InsertAd procedure on the AdConfirm.asp page.

  7. The InsertAd procedure checks for errors and displays a message if it finds any. If no errors occurred, AdConfirm.asp commits the transaction and displays a confirmation message.

For More Information