ASP Fundamentals

Using an ASP object called Response, ASP code can write text to the client. Any content may be written to the client, including code that is executed on the client. IIS distinguishes between code executed on the server and content being sent to the client by using the ISAPI ASP.DLL extension to parse the ASP file for the start tag <% and the end tag %> and using WSH to execute the code in between the start and end tags.

Figure 12-7 shows an example of editing an ASP page using Visual Interdev. Within the same ASP page are lines of content that are being written to the client using the response object and code being executed on the server.

click to expand
Figure 12-7: Editing an ASP page using Visual Interdev

The first line of code, <%@ Language=VBScript %>, tells the ASP ISAPI extension ASP.DLL in IIS to start looking for code to interpret. In this particular case, the language that should be used for processing the ASP is set for the ASP page, and then the %> at the end tells ASP.DLL to ignore subsequent lines of code except for a <% tag. ASP.DLL does not interpret the HTML lines that follow. Referring to Figure 12-7, when ASP.DLL encounters the <% tag after the line that has the <hr> tag, the subsequent lines of code are interpreted by ASP.DLL until the ending tag %> is encountered after the line Response.Write("what time is it ? ->" & now()). The result of a client web browser requesting MyAspFile1.asp, the ASP page shown in Figure 12-7, after having been requested and displayed in a browser, is shown in Figure 12-8.

click to expand
Figure 12-8: Running ASP that uses response.write

ASP Objects

ASP offers the following five objects that support the programmer’s need to produce a solution that responds to a web request event:

  • Response object Used to write data into the HTTP response that is returned to the client

  • Application object Contains settings and configurations designed to affect how ASP works for a given web instance in IIS

  • Request object Contains the content of the HTTP request and offering support functionality to the developer for parsing HTTP request data

  • Server object Contains information about the web server and the web server instance and providing support for calling software

  • Session Represents the state of a given web session with a given host and client

Response Object

Like all the ASP objects, a class instance of the Response object is always available to a given ASP during a web-based request event. The purpose of this object is to support the programmer’s need to write data to the client during a web request. The Write function of the Response object returns to the client the content of the argument passed into Write. The Response object also provides a developer the means by which to control how things are written to the HTTP response.

Application Object

The application object is used primarily in the global.asa file to capture events of the ASP application. Four events may be captured by the application object:

  • Session_OnStart Triggered on the first request from a given user for the web instance

  • Session_OnEnd Triggered when the session expires for a given user

  • Application_OnStart Triggered when any user requests any page for the first time

  • Application_OnEnd Triggered when IIS is being shut down

The application object may also be used to store variables that will keep their state for the entire time the web site is running. Since the application object remains instantiated from the time the first request is made to the web application to the time the web server is shut down, the application object may be used to store data that must be maintained for all page requests and all web sessions.

Request Object

The Request object serves the programmer’s need to obtain information from the client for a given request to the web server. The type of information available in the Request object that is useful to the developer is data that is submitted to the web server. For example, if a user’s name was needed, the ASP would have to request the web user to enter it into a form and click on a submit button. After the web user clicks the button, the ASP would have to look for the information using the Request object. As shown in Listing 12-1, the Request object uses the name of the HTML input element that contains the value the web user enters for their user name.

Listing 12-1: AskName.asp - Sample ASP Requesting User's Name

start example
 <%@ Language=VBScript %> <html> <head> <title>What is you name?</title> </head> <body> <FORM METHOD = "POST" ACTION = "askname.asp" ID = "frmImage"  NAME = "getInfo" > <%  If len(Request("UserName")) > 0 then   Response.Write("<p>you said your name was: " & _                       Request("UserName") & "</p>")      Response.Write("<p>Please reenter it</p>") else   Response.Write( _   "<p>Please enter you name and click the submit button.</p>") end if %> <input TYPE="text" NAME="UserName" VALUE=""> <INPUT  NAME="subR" TYPE="submit" VALUE="Refresh" > </FORM> </body> </html>
end example

When the web user requests the ASP shown in Listing 12-1, he or she will be prompted to enter their name the first time. If the user types any text into the input box named UserName and clicks the submit button, the value entered into the input box is submitted to the web server. Clicking the submit button submits the request to the same ASP that created the page displayed to the user. The name of the ASP is askname.asp, as evidenced by the FORM element’s ACTION attribute. When askname.asp is requested from the web server, it uses the Request object to seek out the value of the input box named UserName using the following line of code:

Request("UserName")

Collections of the Request Object

The Request object may be used to request a value from the query string, an HTTP post, or a cookie using this method of simply naming the value of the variable. The Request object avails to the programmer three collections of values:

  • Request.Cookies Collection of cookie names and values on the client machine

  • Request.Form Collection of HTML input elements names and values resulting from an HTML form submission or post to the ASP file

  • Request.QueryString Collection of name and value pairs in the query string used to request the ASP file

As all collections support the Count, Key, and Item properties, any one of these collections support the ability to refer to a value using its ordinal value. This is useful in situations for which various combinations of data could be parsed to determine the means for processing the data. Logic could be built into the ASP such that if a key of a particular name is submitted, other parameters could be sought out and processed in the request. For an example of an ASP that will list all the values in all the collections, refer to Listing 12-2.

Listing 12-2: ShowRequestValues.asp - ASP Listing Collection Contents

start example
 <%@ Language=VBScript %> <%  '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 'sub WriteCollection 'loops through the contents of any collection object  'passed in and response.writes the index and name = value 'using HTML ' 'in  '      c = any 1 based collection  '     CollectionName = any string to describe the  '     collection 'out – nothing – writes to client '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sub WriteCollection(c, CollectionName)      dim i               'index for collection loop      dim sPhrase          'phrase assembled to show values            'draw a line      Response.Write("<hr><p>")      'announce the size of the collection      Response.Write(CollectionName & " have " & c.Count & _                                            " values:</p>")            for i= 1 to c.Count            'these collections are 1 based -            'I don't know why when most all others are 0 based                       sPhrase = "#" & i & " " & c.Key(i) & _                     " = " & c.Item(i) & "<br>"           Response.Write(sPhrase)      next        end sub %> <HTML> <HEAD> <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0"> <title>Tell me about the requester</title> </HEAD> <BODY> <%  'show me the server time so I can verify page is not cached Response.Write("<p>server time: " & now() & "</p>") 'write cookies collection WriteCollection Request.Cookies, "Cookies" 'write form variables collection WriteCollection Request.Form, "Form Variables"  'write query string collection WriteCollection Request.QueryString, "Query String"  %> </BODY> </HTML>
end example

Listing 12-2 features the use of a subroutine named sub WriteCollection(c, CollectionName) that takes a collection argument and prints the names and values of all the elements within the collection to the requesting browser. The collection’s index must start from 1 as opposed to 0 as many other collections are indexed from. The WriteCollection subroutine is called three times from the body section of the HTML section. In each respective call, a collection of the Request object is passed into the subroutine. On running the ASP file ShowRequestValues.asp, Listing 12-2, the output with sample cookie values and query string values, may be seen in Figure 12-9.

click to expand
Figure 12-9: Output of request collections

Two cookies on the client computer made the request to ShowRequestValues.asp write out the following lines to the browser:

#1 TheServer = Name=birdhouse&Number=2 #2 Visits = LastTime=3%2F22%2F2003+8%3A55%3A02+AM

One cookie is named TheServer and the other cookie is named Visits. Cookie TheServer has multiple values, which is why the output lists the name of the cookie as being equal to the parameter named Name, which in turn is equal to the value birdhouse. This behavior exists because the Request.Cookies collection’s Key property value is the name of the cookie and the Item property value of the cookies collection is the name/value pair. The Request.Form and the Request.QueryString collections are simply made up of name/value pairs that are contained in the Key and Item property values, respectively.

Managing Content Cache Using HTTP Headers

Web browsers occasionally cache web pages or content that is part of a web page. The reason the server time was written to the browser in Listing 12-2 was to be able to determine whether the output of ShowRequestValues.asp was being cached. Caching content improves performance of a web site; however, at times using caching may be undesirable. Some browsers might not write out the new data from ASP code or show the correct image if the names are the same as the files cached from a previous request from the same server and the image changed since the previous request. Using the Response object AddHeader command, an argument could be added to the HTTP response that is sent back to the requesting web browser telling the browser not to cache the content on the page. When the requesting web browser parses the HTTP response, it should encounter the header or headers that indicate that the content should be downloaded instead of obtained from the web browser’s cache.

Refer to Listing 12-3 for a sample of the headers that should be added to the top of the ASP file to cause the web browser not to use cached content.

Listing 12-3: Using AddHeader Function

start example
 <%@ Language=VBScript %> <%  'eliminate caching Response.AddHeader "pragma","no-cache" Response.AddHeader "cache-control","private" Response.CacheControl = "no-cache" %>
end example

The same effect can be achieved, more simply, by adding Response.Expires=-1 to the page.

Managing Application State Using Cookies

Cookies provide a mechanism for storing state data on the web browser’s host. The data may be obtained at a later date from the user’s computer. Writing cookies may be performed using the Response.Cookies function. Listing 12-4 shows an ASP that lists existing cookies and updates them. Cookies may be written with an expiration date. If no date is specified, the cookies last as long as the user keeps the web browser open. When the web browser is closed, the cookies are flushed from the web browser’s host.

Listing 12-4: WriteCookies.asp - ASP Listing and Updating Cookies

start example
 <%@ Language=VBScript %> <%  'need to do this because we are writing cookies Response.Buffer = True   %> <!--#include file="CommonSubs.inc"--> <HTML> <HEAD> <title>Let me mark the time you were here last</title> </HEAD> <BODY> <p> Here are the cookies that were here before </p> <% 'write cookies collection WriteCollection Request.Cookies, "Cookies" 'get time and date for right now dim sTimeNow sTimeNow = now() 'get previous number dim sCookie dim i 'get cookie value sCookie = Request.Cookies("TheServer")("Number") 'make it a number and increment i = cint(sCookie) + 1 %> <hr> <p> The Visits cookie LastTime value will be updated to  <% =sTimeNow %> </p> <p> The TheServer cookie Number value will be updated to  <% =i %> </p> <% 'update cookie values Response.Cookies("Visits")("LastTime") = sTimeNow Response.Cookies("TheServer")("Name") = "birdhouse" Response.Cookies("TheServer")("Number") = i %> </BODY> </HTML>
end example

Listing 12-4 shows an ASP file, WriteCookies.asp, that reads and writes cookies each time the file is called. The values of the cookies are read using the Request.Cookies collection and written to the browser using the WriteCollection subroutine for the end user to see the values. After the values are written to the browser, the ASP reads the value of the TheServer cookie Number value and increments it by one. The new value of the TheServer cookie Number value and the Visits cookie LastTime value are written using the Response.Cookies(<cookie name>)(<parameter name>) = <new value> form of the command.After the language specification code block in Listing 12-4 is the Response.Buffer = True line. This command makes certain that the entire HTTP response, including headers and content, is assembled prior to being written to the client’s browser. When the page that is to be sent to the requesting browser is completely assembled, the ASP ISAPI extension ASP.DLL will first send the necessary headers to set the cookies and then will send the content afterward. Headers must be written prior to sending the content because of the constraints of HTTP. If the buffer setting for the Response object were not turned on, the content would be streamed to the client as it was being generated and headers could not be sent to the client. Listing 12-4 also demonstrates the use of the include file. On line 7, the command <!--#include file="CommonSubs.inc"--> inserts the contents of the file CommonSubs.inc into the ASP file as though it were typed in at that place on file. The subroutine WriteCollection, shown in Listing 12-2, was placed in the file CommonSubs.inc. The great advantage of using an include file in this way is that many common functions may be placed into a single file and used by many other files in a given web application without duplicating code. Maintaining a single copy of code that is common to many ASP files is much easier than writing a separate version of the same function or subroutine of code in each file. Fixing a bug to a subroutine in an include file instantly makes the fix everywhere the include file is, well, included. If separate versions of the same function or subroutine of code were used in many ASP files, the fix for the subroutine or function would require more work and there would be greater risk of introducing a new bug while attempting to fix the existing bug, because so many files would require editing.

Another ASP technique that Listing 12-4 demonstrates is the use of the = to perform a Response.Write. Refer to this line:

<p> The Visits cookie LastTime value will be updated to  <% =sTimeNow %> </p>

The code <% =sTimeNow %> opens the ASP code block in the HTML section of the ASP long enough to write the value of the variable sTimeNow. The = is a shorthand way of performing the Reponse.Write command immediately following the <% tag. The intention for its use is for situations such as inserting Response.Write in HTML code so that it is easier to follow and read.

Session Object

The Session object is a turnkey state management solution that uses cookies. The Session object manages session time and information about the end user in a way that insulates the programmer from the effort of reading and writing cookies and checking time and values for the session. If the client computer does not allow cookies to be used on the machine, the Session object will not be able to write the session cookie and it will not function properly. The cookie written by the Session object has no expiration date set, so it is flushed when the browser is closed. The Session object holds all the session data in a collection named Session.Contents. The Session object and the Contents collection can be managed and manipulated just like the other ASP object collections. WriteCollection, a custom subroutine shown in Listing 12-2, may be used to show the contents of the Session.Contents collection in the exact same way that the cookies, Form variables, and QueryString collections were written to the browser. For an example of code that uses the Session object in a way similar to the Request.Cookies collection, refer to Listing 12-5.

Listing 12-5: ReadWriteSession.asp - ASP Page Using the Session Object

start example
<%@ Language=VBScript %> <!--#include file="CommonSubs.inc"--> <% '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 'sub UpdateSession 'Performs a simple Session object read and write ' 'obtains the current time and the previous Session    'value for Number. The value for Number is incremented. 'All new values are written to the Session. ' 'in  '      oSession = any 1 based collection - specifically  '     Session Object '     NewTime = new time generated written to Session '     NewNumber = new number written to Session '      'out - nothing - writes to client.   '     NewTime, NewNumber are updated. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sub UpdateSession(oSession, NewTime, NewNumber)      dim sSession            'get time and date for right now      NewTime = now()      'get session value      sSession = oSession("Number")      'make it a number and increment      NewNumber = cint(sSession) + 1            'write the new values      oSession("Number") = NewNumber      oSession("LastTime") = NewTime      oSession("Name") = "birdhouse"       end sub %> <HTML> <HEAD> <title>managing a session using the session object</title> </HEAD> <BODY> <p> Here is the session prior to update</p> <% dim sTime dim sNumber 'write Session collection WriteCollection Session.Contents , "Session Contents" 'write some other sundry values Response.Write("<br>Session.SessionID:" & Session.SessionID) Response.Write("<br>Session.Timeout:" & Session.Timeout) 'update session and get new values that are written to session UpdateSession Session.Contents, sTime, sNumber 'set session timeout to 1 minute - default is 20 minutes Session.Timeout=1 %> <hr> <p>The session LastTime value will be updated to <% =sTime %></p> <p>The session Number value will be updated to <% =sNumber %></p> </BODY> </HTML>
end example

Listing 12-5 is an ASP that, when run, reads the values of the Session object, updates the values, and writes the new values to the Session object. The WriteCollection subroutine is in the include file specified in the line <!--#include file="CommonSubs.inc"-->. Two other properties unique to the Session object, SessionID and Timeout, are written to the browser. The UpdateSession subroutine is called using the Session.Contents collection, using the variables sTime and sNumber as arguments. The sTime and sNumber variables are empty variables that will be filled in the subroutine with the actual values written to the session. After Session.Timeout is set to 1 minute, the information about the updates made to the session are written to the browser.

In VBScript, by default, all parameters in a function or subroutine are passed by reference. This means that the values of the arguments will change as a result of any manipulations that take place in the function or subroutine to the arguments passed in. In the UpdateSession subroutine, sTime and sNumber contain no values when they are passed into UpdateSession as arguments. In the subroutine, sTime is set to the current time using the now() function, and the sNumber variable is set to the previous value and has 1 added to it. After the subroutine has completed running, both sTime and sNumber are holding the new values to which they were set in the function UpdateSession. Using the keyword byval in front of any parameter in the subroutine prototype declaration, an argument being passed in would not be changed after the subroutine completes execution.

When the ASP ReadWriteSession.asp shown in Listing 12-5 is run for the first time, it shows values of the Session object in the browser. The Session.Timeout property is set to 20 by default and no values are set in the session until the ASP completes execution. Figure 12-10 shows a sample output of the session ASP code on the first run.

click to expand
Figure 12-10: Output from default session

Refreshing ReadWriteSession.asp causes the ASP to run again and update the session object with new values, and it prints the values to the web browser, as shown in Figure 12-11. The Session object can hold values associated with a name in the same way that a cookie does, thereby allowing the Session object to be used as a mechanism for maintaining application state between page requests. The cookies printed out to the browser in Figure 12-9 were added to the Session object in this example to demonstrate the similarities in functionality between storing and retrieving data from the session or a cookie. ReadWriteSession.asp also sets a new Session.Timeout value of 1 minute.

click to expand
Figure 12-11: Default Session updated with new Session.Timeout property and values

Session Object Limitations

The Session object has some limitations that should be considered before you build ASP state management around the exclusive use of the Session object. The session is not replicated or shared among servers working in a web farm. The session data is actually stored on the hosting server that supplied the session. When the user requests an ASP page for the first time from a given web server using the ASP session, a session cookie is written to the client’s host. The session cookie holds a single value that acts as a key. When the end user requests another ASP from the web server, the ASP ISAPI extension ASP.DLL gets the session cookie and looks up the data that will populate the Session object using the key that was obtained from the session cookie. If the ASP is served from another web server participating in a web farm environment, the session data that was present on the web server from which the session cookie was created will not exist on that host, so the session will not be loaded.

To use the ASP Session object as it is intrinsically provided, no more than one server may be utilized. Alternatives and solutions to this problem detail the creation and use of a session server that maintains session data for web servers participating in a web farm. The member hosts participating in the web farm can obtain requestors’ session data from the session server whenever it is needed. Details about these types of solutions are beyond the scope of this book but should be considered and examined in a web farm environment that is hosting an ASP solution using the Session object. ASP.NET does offer an easier solution that is an intrinsic part of the .NET Framework; this is covered in Chapter 14.

Server Object

A common use for ASP is to obtain some data from a data store, apply some business logic to it, and display it to the user in the web browser. Performing all of these tasks solely using ASP is possible, but many web programmers use other software libraries for this purpose. For example, ASP offers no intrinsic library for reading data sources. The Server object does support a function for creating an object instance of a COM object, however. The object instance can be any COM object that has an interface that is readable using VBScript. A popular COM object or set of objects useful for reading data sources is called Active Data Objects (ADO).

Data Access Using ADO

ADO is designed to offer a programmer a universal data access solution for all data sources. Ideally, ADO acts as a layer of abstraction between the business logic code and the data source in any solution. By changing a set of parameters that dictate the data source location, the software should still read and write to the data source without requiring a code change. Currently, ADO offers this type of abstraction among data sources of similar data structure architecture. For example, the ADO code used to read and write to and from a SQL Server database could also read and write to and from an Oracle database without requiring a code change.

Legacy Data Access Technology Microsoft also provided other data access libraries, such as Data Access Objects (DAO) and Remote Data Objects (RDO). DAO offers a great deal of functionality for the developer to manage and parse a set of data pulled from a database, but it does not offer the ability to asynchronously query a client-server database hosted on a networked server. RDO offers less functionality for parsing and managing data pulled from a database, but it does offer a light set of software that allows a developer to pull data efficiently from a client-server database hosted on a networked server. Both DAO and RDO technologies are considered legacy and are no longer supported, so they should not be used. ADO offers the programmer the best of all situations, and it has become the marquee of the Microsoft data access technology. ADO provides the developer many options for managing and parsing data pulled from the database as well as many options for establishing the connection to the database.

Using ADO in ASP ASP code accessing data in a database to display a recordset must use the Server.CreateObject command to use ADO. All COM objects have a name, and they are registered on the host server. The name feature of the COM object eliminates issues related to locating the proper library by abstracting the library location on the host from the effort of referring to the library. A COM object needs to be specified only by its name. The Server.CreateObject command takes the name of the COM object in the form <library name>.<class name> as the argument to the function. An instance of the COM object is returned to the variable that is set to the function call.

Because ADO is a group of COM objects, the Server.CreateObject command is used to create the various objects used in the course of connecting to a database, reading recordsets from the database, and writing the recordset values to the web browser. For a sample ASP that reads a simple recordset from a database table and writes the data to the web browser, refer to readDB.asp in Listing 12-6.

Listing 12-6: readDB.asp - ASP Opening a Recordset from a Database and Printing the Contents to the Web Browser

start example
 <%@ Language=VBScript %> <HTML> <HEAD> <title>read a database table</title> </HEAD> <BODY> <% dim myConn  dim myRecordset dim sSQL dim sResult dim sConn 'For database connection string       'create connection string       sConn = "Provider=Microsoft.Jet.OLEDB.4.0;"       sConn = sConn & "Data Source=C:\aspData\employees.mdb;"            'establish connection      Set myConn = Server.CreateObject("ADODB.Connection")      myConn.Open sConn            'create the recordset      set myRecordset = Server.CreateObject("ADODB.Recordset")      'build SQL command      sSQL = "SELECT first_name, last_name FROM tblEmployee"      'open the recordset from the database      myRecordset.Open sSQL, myConn      'check if there are any values in the recordset      if not( myRecordset.EOF and myRecordset.EOF) then                 'loop through the recordset until the end record           while(not myRecordset.EOF)                'build output string                sResult = myRecordset("first_name") & " "                sResult = sResult &  _                         myRecordset("last_name") & "<br />"                               'write value to browser               Response.write(sResult)                               'advance recordset               myRecordset.moveNext()           wend                 else           'let us know that there were no values           Response.write("nothing returned")      end if            'shut down recordset and connection objects      myRecordset.Close()      myConn.Close() %> </body> </html>
end example

ADO Connection Object The first object created in Listing 12-6 is an ADO connection object. The ADO library is named ADODB and the class is named Connection, so the command to create an object is

Set myConn = Server.CreateObject("ADODB.Connection")

The variable myConn points to an instance of the ADO Connection class. The Connection class maintains and passes the access credentials to the data source. In Listing 12-6, an Object Linking and Embedding database (OLE-DB) string was created to open a database connection to a Microsoft Access database hosted on the server.

Microsoft’s Access Database Using a Microsoft Access database on the same host as the web server is a good way to provide very fast data access on small data sets—say, data sets smaller than 300 MB. Your results for performance versus dataset size may vary considerably depending on the host and the application, so you will have to load test to determine the performance limitation. Using an Access database file costs nothing, although you do have to purchase Microsoft Access if you want to manipulate the data using a tool rather then making SQL commands in ASP code. Microsoft Access can also provide faster data access times, too, on small datasets than the costly enterprise database products like Microsoft’s SQL Server or Oracle’s database products.

Microsoft Access has many limitations when it is used to manage large datasets, however. Microsoft Access also does not offer the same kind of functionality as do other database products such as SQL Server or Oracle. Microsoft Access is like a glorified file read-and-write utility since the resource to query data is provided by the consuming application. In the case of an ASP, the resource to query data is drained from IIS. In stark contrast, however, the resource to perform a query in an enterprise level database will be borne by the host. The permissions to access a Microsoft Access database are set using file permissions. In the examples shown in this chapter, the IIS Windows service account named IUSER_<machine name> (where machine name is the name of the host) must be assigned read and write access to the Microsoft Access .mdb database file. The Microsoft Access file has an .mdb extension and may be identified in the OLE-DB connection string.

ADO Recordset Object Another ADO object, called a recordset, is also instantiated. The recordset object supports the programmer’s need to access the data in a dataset. It gives the programmer the ability to “touch” the data by supporting the need to read and write data to and from the data source. The recordset is opened using a SQL command and the instance of the connection object myConn. After the Open function of the recordset object is called, the recordset can be traversed and written to the browser. Before this exercise can be performed, however, the recordset must be validated to determine whether any records are in the recordset. A recordset is empty if the BOF (beginning of file) and EOF (end of file) conditions are both true.When a recordset is first opened, it may be safe to assume that the recordset is pointing to the first record. The recordset may be advanced using the moveNext function of the recordset object. For each call of the moveNext command, the EOF condition must be inspected. If EOF is true, the recordset has been traversed to the end. Calling moveNext after EOF is encountered will result in an error. As long as the EOF is not true, the recordset may be advanced to the next record. When the database interaction is completed, both the recordset and the connection objects should be closed.

The result of running Listing 12-6 is a display in the browser of the first name, a space, and the last name for each record in the database table tblEmployee. The ASP file could be improved to support end-user submission of data to the data table. Refer to Listing 12-7 for a part of an ASP, ReadWriteDB.asp, that lists the data in tblEmployee and allows the user to submit additional data for writing into the database.

Listing 12-7: Part of ReadWriteDB.asp Showing Main Entry Point of ASP

start example
 <HTML> <HEAD> <title>read and write to a database table</title> </HEAD> <BODY> <% '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 'main entry point to ASP '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ dim myConn 'For ADO connection  dim sSQL 'For SQL command dim sConn 'For database connection string       'create connection string       sConn = "Provider=Microsoft.Jet.OLEDB.4.0;"       sConn = sConn & "Data Source=C:\aspData\employees.mdb;"      'establish connection to database server      Set myConn = Server.CreateObject("ADODB.Connection")      myConn.Open sConn            'get parameters from user submission      AddData myConn            'build SQL command to show what exists in database      sSQL = "SELECT * FROM tblEmployee"      'show the data      ShowUserRecordset sSQL, myConn %> <FORM METHOD = "POST" ACTION = "ReadWriteDB.asp"  ID = "frmFields" NAME = "getInfo" > <%       'build the data entry table      MakeDataEntryTable sSQL, myConn            'close up connection since we are done      myConn.Close()       '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 'End of ASP '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      %>      <INPUT  NAME="subR" TYPE="submit" VALUE="Add" > </FORM> </body> </html>
end example

The file ReadWriteDB.asp has three other subroutines that were excluded from Listing 12-7 for the sake of brevity. Listing 12-7 shows the main entry point of the ASP ReadWriteDB.asp, where the ASP.DLL ISAPI extension begins to execute the server-side code. The additional subroutines used in ReadWriteDB.asp that were excluded from Listing 12-7 are as follows:

  • sub ShowUserRecordset Using the SQL command and the ADO Connection object instance passed in, this subroutine prints the contents of the recordset to the browser.

  • sub MakeDataEntryTable Using the SQL command and the Connection object instance passed in, this subroutine prints a table with the field names for the headings and inputs for the fields to the browser.

  • sub AddData This subroutine checks the Request object for data submission and inserts the user submitted data into the database table tblEmployee.

Writing Form Submission Data to a Database: sub AddData

After the comment block labeled main entry point to the ASP, the ASP code will instantiate the ADO database connection object and assign a variable named myConn to point at the object. The ADO connection will be passed into each subroutine so that each subroutine will be able to create their own ADO objects and connect them to the data source. After the connection object is instantiated, the AddData subroutine, shown in Listing 12-8, is called to check for data being submitted to the ASP.

Listing 12-8: Part of ReadWriteDB.asp - AddData Subroutine Writing Data to Database

start example
 '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 'sub AddData 'Checks Request Object for data submission and inserts 'the user submitted data into the database table  'tblEmployee '  'in  '     oConnection = ADO connection object 'out - nothing - writes to client '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sub AddData(byval oConnection) 'check for data to write if ((len(Request("number"))< 1) or _      (len(Request("phone"))< 1) or _      (len(Request("first_name"))< 1) or _      (len(Request("last_name"))< 1)) then      exit sub end if            dim myCommand dim sSQL            'create the Command object      set myCommand = Server.CreateObject("ADODB.Command")      set myCommand.ActiveConnection = oConnection            'build the insert statement      sSQL = "INSERT INTO tblEmployee ("      sSQL = sSQL & "[number], [phone], [first_name], [last_name])"      sSQL = sSQL & " VALUES ('" & Request("number")       sSQL = sSQL & "','" & Request("phone")       sSQL = sSQL & "','" & Request("first_name")       sSQL = sSQL & "','" & Request("last_name")& "')"      'put the command in the command object      myCommand.CommandText = sSQL            'execute the query      myCommand.Execute             end sub
end example

In the subroutine AddData, the Request object is checked for the expected parameters, which in this case are number, phone, first_name, and last_name. If any one of the parameters is missing, the subroutine is exited. The existence of the parameters is determined by checking whether the length of any one of them is less than one character. If all parameters are at least one character in length, the ADO command object will be instantiated.

Using the Command Object The ADO command object is useful for executing SQL commands or performing other types of tasks, whereby a command is sent to the database server but a recordset response is not always needed. The command object can return a recordset, too, but as in the case whereby it is being used in this example, an Update SQL command will be passed to the database for execution. The SQL command is created by concatenating all of the parts of the string necessary to generate an update SQL statement that looks like the following:

INSERT INTO tblEmployee ([number],[phone],[first_name],[last_name]) VALUES ('8','444-4444','Tosa','Bergstaff')

The values cited in the VALUES section of the SQL command are extracted from the request object. After the SQL command is assembled, it is stuffed in the Command object’s CommandText property and fired by using the Command object’s Execute function. If everything goes well, the values should be written to the database. Failure generally means that some entity involved in the database transaction, like a server, was not present or identified incorrectly. This type of failure can stem from network connectivity problems or from the developer incorrectly typing the name of a table or field in the code.

Code Practices and “Hardcoding” The subroutine AddData has a limited scope of service, because it has the name of the table that it updates and the parameter names that are being requested from the Request object written in the code as well. Many programmers refer to this practice as hardcoding, or the values may be referred to as magic values or literals. In general, the practice of hardcoding is performed for the sake of expediency and reliability of execution, but most often it should be avoided. Literals that are dependent on a given deployment of the application should be replaced and set up as configuration settings for the application that may be changed without requiring a code edit.

Reading Data from Database to Display in a Browser: sub ShowUserRecordset

After the submitted data is written to the database, ReadWriteDB.asp builds a SQL command to read all the data from the table tblEmployee. The SQL command is assigned to the variable sSQL. The connection object MyConn and the SQL command sSQL are both passed to the subroutine named ShowUserRecordset. Both arguments are passed byval, so there is no chance that their value will be altered in the subroutine. ShowUserRecordset will accept any SQL command and ADO Connection object, and it will open a recordset on the SQL command and write out the values in the recordset to the browser. Refer to Listing 12-9 for the code in the subroutine ShowUserRecordset.

Listing 12-9: Part of ReadWriteDB.asp - ShowUserRecordset Subroutine Reading Database and Showing Recordset

start example
 '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 'sub ShowUserRecordset 'Using the SQL command and the Connection object  'instance passed in, prints the contents of the  'recordset to the browser ' 'in  '      sSQL = SQL command for recordset    '     oConnection = ADO connection object 'out - nothing - writes to client '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sub ShowUserRecordset(byval sSQL, byval oConnection) dim myRecordset dim sResult dim oField      'create the recordset      set myRecordset = Server.CreateObject("ADODB.Recordset")      myRecordset.Open sSQL, oConnection      'write out the headings      for each oField in myRecordset.Fields           sResult = sResult & oField.Name & "         "      next       Response.Write(sResult & "<br/>" )            'check to see if there are records      if( not myRecordset.EOF and not myRecordset.EOF) then                      'make certain that we are not at the end           while(not myRecordset.EOF)                            'build output string                sResult=""                for each oField in myRecordset.Fields                 sResult = sResult & myRecordset(oField.Name) & " "                 next                 sResult = sResult & "<br />"                                'write value to browser                Response.write(sResult)                                'advance recordset                myRecordset.moveNext()           wend      else           Response.write("No records in table <br>")      end if            Response.write("<hr>")      'shut down recordset and connection objects      myRecordset.Close() end sub
end example

ADO Recordset Traversing Techniques ShowUserRecordset in Listing 12-9 is a superior subroutine compared to AddData because the SQL command is passed into the subroutine instead of being hardcoded in the subroutine. ShowUserRecordset still has flaws because it contains literals that relate to logic display and the COM object name for the ADO recordset COM object. After an instance of an ADO recordset is created and opened using the SQL passed in via the sSQL parameter, the field names of the recordset are obtained and listed into a string variable named sResult. The purpose of sResult is to assemble text that will eventually be written to the browser using the Response.Write command. The ADO recordset supports a collection called Fields, which contains all of the fields that make up the recordset. A technique was used to loop through all of the fields in the collection that could have been used in the WriteCollection subroutine demonstrated in Listing 12-2. A variant may be used as an index value in a For…Next loop that uses this pseudo-code form:

For each <variant> in <a collection>

As all variables in VBScript are variants, any declared variable could act as a suitable indexing variable for a collection. The variable oField specifically was declared for the purposes of acting as an index in the For…Next loop used to access the Fields collection of MyRecordset in ShowUserRecordset. After the resulting string of field names is written to the browser, the recordset is checked for records and a While…Wend loop is started to loop through the records. Embedded in the While…Wend loop is another For…Next loop using the MyRecordSet Fields Collection. The Fields Collection loop is used to specify the field name in the recordset for the purposes of obtaining a value that may be appended to the sResult string. Each iteration of the While…Wend results in a line being written to the browser that represents a single row from the recordset that was opened.

Note 

A common problem for many programmers, even after many occasions of writing software code that loops through a recordset, is that they forget to advance the recordset. The line myRecordset.moveNext() advances the recordset in Listing 12-9. If the moveNext() line was left out, the code would begin an infinite loop. This mistake is a common one, because the moveNext() command requires little thought on the part of the coder. It is one of those issues that requires so little thought as to the strategy for placing it that it is often forgotten.

When the recordset has been traversed and the content of the recordset is written to the browser, the recordset should be shut down using the close() command of the recordset object. The instance of the recordset is destroyed when the variable pointing to the instance of the myRecordset goes out of scope. The variable goes out of scope when the subroutine ShowUserRecordset ends. Relying on the resources supporting the class instance being freed from memory when the variable goes out of scope is not a good idea, however, because the resource is not always freed right away when it is shut down in this manner. The close() command should be used to ensure that the database connection is closed and the resources that the connection was occupying may be used elsewhere.

Parsing ADO Recordset to Build Data Entry Form: sub MakeDataEntryTable

After the ShowUserRecordset subroutine is called, the ReadWriteDB.asp places an HTML form element on the browser page so that the end user can make a submission to the server. The purpose for the AddData subroutine is to accept the data submitted from the HTML form on the web page. The HTML form used for collecting the data from the end user must match the form of the dataset being updated. In this particular situation, the inputs for user submission are all named the same as the field names in the database table from which they relate. The subroutine MakeDataEntryTable produces the HTML table that contains the headings representing the recordset field names that are part of the recordset that is generated from the SQL statement passed into the subroutine. This subroutine uses the same looping mechanism as the subroutine ShowUserRecordset to build the HTML table. Refer to Listing 12-10 for a listing of the subroutine MakeDataEntryTable.

Listing 12-10: Part of ReadWriteDB.asp - MakeDataEntryTable Subroutine Serializing a Recordset into an HTML Table

start example
 '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 'sub MakeDataEntryTable 'Using the SQL command and the Connection object  'instance passed in, prints a table with the field   'names for the headings and inputs for the fields. ' 'in  '      sSQL = SQL command for recordset    '     oConnection = ADO connection object 'out - nothing - writes to client '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sub MakeDataEntryTable(byval sSQL, byval oConnection) dim myRecordset dim sResult dim oField            'create the recordset      set myRecordset = Server.CreateObject("ADODB.Recordset")      myRecordset.Open sSQL, oConnection      'build a table for user to enter values      sResult = "<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 >" &_                                                         vbCRLF      sResult = sResult & "<TR>"            'make the headings      for each oField in myRecordset.Fields           sResult = sResult & "<TH>" & oField.Name & "</TH>"      next             'make a new row      sResult = sResult & "</TR>" & vbCRLF & "<TR>"            'make the user inputs      for each oField in myRecordset.Fields           sResult = sResult & _                     "<TD> <input TYPE=""text"" NAME=""" & _                     oField.Name & """ VALUE=""""></TD>"      next       sResult = sResult & "</TABLE>"      Response.Write(sResult)      'shut down recordset and connection objects      myRecordset.Close()       end sub
end example

Running ReadWriteDB.asp will build a screen on the browser that shows the existing values in the database table, a line, a table with the field names and input boxes in the second row, and an Add button. Clicking the Add button without placing any data in all of the text boxes results in a screen refresh. Placing data in all four input boxes will result in data being written to the database table tblEmployee after the Add button is clicked. Figure 12-12 shows a screen shot of data being entered in ReadWriteDB.asp.

click to expand
Figure 12-12: ReadWriteDB.asp displayed in browser before update

Code Practices—Logic Abstraction and Extendibility The subroutines ShowUserRecordset and MakeDataEntryTable are both written so that they will support any recordset and connection object. They may be used in other applications where the same action and output is desired, without requiring a code edit. The database table may also change shape, and these subroutines will still function without requiring a code edit. The subroutines are still quite limited in terms of their usefulness in other applications, however, because they contain display logic. Most other needs for a web application require an alternative user interface. Abstracting the presentation logic from the subroutines would make them much more useful across a wide scope of applications. Code that is useful across multiple scopes of application is called extendible code. Abstracting various portions of logic generally affords greater extendibility in code but usually at the expense of performance and speed of development.




IIS 6(c) The Complete Reference
IIS 6: The Complete Reference
ISBN: 0072224959
EAN: 2147483647
Year: 2005
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net