The relational data access examples in the previous chapter were fairly simple, concentrating on extracting data from a single table and multiple tables into the DataSet and DataReader objects. However, often the results you want are not just rows from a single table. They may require a more complex SQL query that joins several tables, or they might be the result of running a stored procedure within the database.
This section shows some examples that use both complex SQL statements and stored procedures to return sets of rows or just individual values from a data source. The first shows how you can use a DataReader object to efficiently extract the data for display, and the second uses the DataSet object.
You can use the DataReader object to quickly and efficiently extract a rowset from a data store. Simply create a Connection object, use it to create a Command object for this connection, and then call the ExecuteReader method of the Command object. It returns the new DataReader object.
The example code, like many of the relational data access examples in the previous and in this chapter, uses one of the connection strings that are defined in the web.config file in the root folder of the samples. Remember to edit this file to suit your own setup (as demonstrated in Chapter 8) before running the samples on your own server.
Also, the example pages in this chapter use server-side <div> elements to display the connection string, the SQL statements being used, and any error message. Many pages also use the ASP DataGrid control to display the results. For example, the following code shows the relevant controls within the HTML section of the example page:
<div>Connection string: <b><span id="outConnect" runat="server"></span></b></div> <div>SELECT command: <b><span id="outSelect" runat="server"></span></b></div> <div id="outError" runat="server"> </div> <asp:datagrid id="dgrResult" runat="server" />
Figure 9-2 shows what the example page that opens from the link in the main menu Extracting Complex Data with a DataReader ( complex-datareader.aspx ) looks like when it runs:
Note | All the examples contain a [view source] link at the bottom of the page that you can use to view the source code for the page. |
The code for this page is compact and quite simple. An OLEDB provider is used to access SQL Server in this example, so you need to use the OleDb -prefixed objects from the System.Data.OleDb namespace of the .NET class libraries. Add the appropriate Import declarations to the head of your page so that they are available, as shown at the start of the following code.
The most complex part is the SQL statement itself, which selects data from two joined tables. In the Page_Load event collect the connection string from the user control we discussed earlier, and use it in the constructor for a Connection object. Also create the SQL statement in a string variable named strSelect :
<%@Import Namespace="System.Data" %> <%@Import Namespace="System.Data.OleDb" %> ... Sub Page_Load() 'get connection string from web.config file Dim strConnect As String strConnect = ConfigurationSettings.AppSettings("DsnWroxBooksOleDb") outConnect.innerText = strConnect 'and display it 'specify the SELECT statement to extract the data Dim strSelect As String strSelect = "SELECT BookList.*, BookAuthors.FirstName, " _ & "BookAuthors.LastName FROM BookList INNER JOIN " _ & "BookAuthors ON BookList.ISBN = BookAuthors.ISBN " _ & "WHERE BookList.ISBN LIKE '07645439%'" outSelect.innerText = strSelect 'and display it
Note | If you want to use the direct (TDS) driver for MS SQL Server, you will need to import the System.Data.SqlClient namespace instead of System.Data.OleDb , and use the objects prefixed with Sql , as demonstrated in the previous chapter. Also remember to use the DsnWroxBooksSql value from web.config for the connection string instead of the DsnWroxBooksOleDb value. And if you are using the ODBC driver you will need to import the System.Data.Odbc namespace and use the objects prefixed with Odbc , and use the DsnWroxBooksOdbc value from web.config for the connection string. |
Now, let's go back to the example code. First declare a variable to hold a DataReader object. Next , create the new Connection object using the connection string and, within a Try..Catch construct create a new Command object using the string that holds the SQL statement, and the Connection object. Open the connection and execute the SQL statement in the Command to return your DataReader object. If there is an error, display the details in the <div> element created in the HTML part of the page, and stop execution of the code:
'declare a variable to hold a DataReader object Dim objDataReader As OleDbDataReader 'create a new Connection object using the connection string Dim objConnect As New OleDbConnection(strConnect) Try 'create new Command using connection object and SQL statement Dim objCommand As New OleDbCommand(strSelect, objConnect) 'open connection and execute command to return the DataReader objConnect.Open() objDataReader = objCommand.ExecuteReader() Catch objError As Exception 'display error details outError.innerHTML = "* Error while accessing data.<br />" _ & objError.Message & "<br />" & objError.Source Exit Sub ' and stop execution End Try
If all goes well and you've got your rowset, you can go ahead and display it. This example uses a DataGrid , but you could just iterate through the rows and create the output that way, as demonstrated in the Using a DataReader Object example in the previous chapter. Finally you must remember to close the connection, and also destroy the DataReader object, although this will be destroyed when the page ends anyway:
'assign the DataReader object to the DataGrid control dgrResult.DataSource = objDataReader dgrResult.DataBind 'and bind (display) the data objConnect.Close() 'then close the connection 'finished with the DataReader objDataReader = Nothing End Sub
So, using a complex SQL statement to access multiple tables is easy enough. In fact, often the hardest part is creating the statement itself. An easy way to do this is to take advantage of the Query Designers in programs like Visual Studio or Microsoft Access, both of which can easily link to a set of database tables in SQL Server and other OLEDB- or ODBC-enabled data sources.
Having seen how to use a complex SQL statement with a DataReader , let's see how the same SQL statement works with a DataSet object. The Extracting Complex Data with a SQL Statement into a DataSet ( complex-dataset.aspx ) example is very similar to the previous DataReader example. The only differences, as shown in the following code, are the declaration of the DataSet object (notice that a DataSet object instance is created with the New keyword here, whereas a variable of type DataReader was created in the previous example), and the use of a DataAdapter object instead of a Command object.
Then, once the DataSet is filled, you can display the contents of the single table within it. Again, you're using a DataGrid to show the results, but this time you have to use a DataView object (as returned by the DefaultView property of the table in the DataSet ) as the DataSource :
'declare a variable to hold a DataSet object Dim objDataSet As New DataSet() Try 'create a new Connection object using the connection string Dim objConnect As New OleDbConnection(strConnect) 'create new DataAdapter using connection and SQL statement Dim objDataAdapter As New OleDbDataAdapter(strSelect, objConnect) 'fill the dataset with data via the DataAdapter object objDataAdapter.Fill(objDataSet, "Books") Catch objError As Exception 'display error details outError.innerHTML = "* Error while accessing data. " _ & objError.Message & " " & objError.Source Exit Sub ' and stop execution End Try 'assign the table DefaultView to the DataGrid control dgrResult.DataSource = objDataSet.Tables("Books").DefaultView dgrResult.DataBind() 'and bind (display) the data
The previous two examples demonstrated how to use complex SQL statements that join data from several tables and return it as a single table or rowset. There is also another situation, where you extract data from the tables in the data source using simple SQL statements and store the resulting rowsets as individual tables (plus the relationships between them) in a DataSet .
In the previous chapter, we showed you a custom user control that creates and returns a DataSet object containing three tables and the relationships between these tables. In that example ( use-dataset control.aspx ), the contents of the DataSet were displayed using several ASP DataGrid controls so that you could see the contents of all the tables.
In an application, however, you generally should be able to access the data in a nested and structured fashion so that it can be displayed in a format that is meaningful to the user. In other words, you want to be able to display it in a hierarchical format, perhaps using some clever type of UI control widget. What we'll demonstrate here is how you can access the data in that way (though you're just going to display it as text in the page in your example).
Figure 9-3 shows the Displaying Nested Relational Data example ( nested- data-access .aspx ). It lists several of the books stored in the sample database, and for each one shows the authors (where available) and the prices in three currencies:
In the previous chapter, you saw how the WroxBooks database holds three tables that contain all the information shown in this page. As shown in Figure 9-4, the BookList table contains the ISBN (the primary key), the title, and the publication date. The BookAuthors table contains the ISBN as a foreign key and the first and last name of each author. There is a row for each author for each book. The BookPrices table holds the ISBN as a foreign key, the currency name ( CAN , GBP , or USD ), and the price in that currency. Again, there is one row for each currency for each book.
The DataSet returned by the custom user control contains three tables named Books , Authors , and Prices , each containing matching subsets of rows from the three tables in the database. It also has the relationships between the tables defined (you can review the code in the previous chapter for creating a multiple-table DataSet to see how it works).
To be able to create the output shown in Figure 9-3, you need a way of navigating from one table to another so that you can pull out the child rows in the Authors and Prices tables in the DataSet object that match each row in the Books table in the DataSet . The technique is to use the GetChildRows method that is exposed by the DataRow object.
All you have to do is iterate through the Books table one row at a time, calling the GetChildRows method twice on each DataRow object “ once to get the matching Authors rows, and once to get the matching Prices rows. Specify the relationship that links the parent and child tables in the call to GetChildRows so that it can work out which rows to return. Each call to GetChildRows returns a collection of matching DataRow objects from the specified child table. You can then iterate through these collections displaying the values of each row.
Note | Interestingly, there is also a converse method named GetParentRows that, in conjunction with the members of the ParentRelations collection of the DataTable object, returns a collection of the matching parent rows when called using a DataRow object that represents a child row and a relationship between the tables. This could be useful if you wanted to list the results in a different order “ perhaps by author instead of by book. |
Let's examine the code for the example shown in Figure 9-3. We won't show the code to build the DataSet here, as it's been described in the previous chapter. We're interested in how to use the GetChildRows method to create and output the nested results. The first step (shown in the code that follows ) is to create a string variable to hold the result (insert it into a <div> on the page in customary fashion afterwards). Then you can get references to all the objects you'll need.
As shown in the next section of code, you need a reference to the Books table to be able to iterate through the rows. Notice also that you just need references to the relationship objects and not to any of the columns “ the GetChildRows method uses the previously defined relationships (which already contain the column information) to figure out which rows you want:
'create a string to hold the results Dim strResult As String = "" 'create a reference to our main Books table in the DataSet Dim objTable As DataTable = objDataSet.Tables("Books") 'create references to each of the relationship objects in the DataSet Dim objAuthorRelation As DataRelation = objTable.ChildRelations("BookAuthors") Dim objPriceRelation As DataRelation = objTable.ChildRelations("BookPrices")
As shown in the following code, you can iterate through the rows in the parent Books table. For each row, extract the values of the Title , ISBN , and PublicationDate columns and add them to the results string. Next, get a collection of the rows that are related to the current row by specifying the reference to the relationship between the Books and Authors tables in the DataSet in your call to GetChildRows . Also add a subheading Author(s) to the results string, and then iterate through the collection of DataRow objects returned by the GetChildRows method. For each one, extract the first and last name of the author, and add it to the results string “ followed by an HTML line break:
Dim objRow, objChildRow As DataRow For Each objRow In objTable.Rows 'get the book details and append them to the "results" string strResult &= "<b>" & objRow("Title") & "</b><br /> ISBN: " _ & objRow("ISBN") & " Release Date: " _ & FormatDateTime(objRow("PublicationDate"), 1) & "<br />" 'get a collection of all the matching Authors table rows for this row Dim colChildRows() As DataRow = objRow.GetChildRows(objAuthorRelation) strResult &= " Author(s): " 'iterate through all matching Authors records adding to result string For Each objChildRow In colChildRows strResult &= objChildRow("FirstName") & " " _ & objChildRow("LastName") & ", " Next strResult &= "<br />"
Repeat the process, but this time using the relationship between the Books and Prices tables. As shown in the following code, extract the currency name and the price for each matching child row and add them to the results string. Then, having completed one book, you can go back and repeat the process for the next book in the parent Books table. After processing all the book rows, present the results in the <div> element named divResults :
'repeat using Prices table relationship for data from Price records colChildRows = objRow.GetChildRows(objPriceRelation) strResult &= " Price: " For Each objChildRow In colChildRows strResult &= objChildRow("Currency") & ":" _ & objChildRow("Price") & " " Next strResult &= "<p />" Next 'and repeat for next row in Books table divResults.innerHTML = strResult 'display the results
So, while you can take advantage of clever client-side display controls such as the ASP DataGrid when working with tables in a DataSet , there is an alternative if you want to create nested output from more than one table. Of course, third-party suppliers are already offering other data grid controls, including those that can bind directly to a DataSet and display the nested data automatically.
So far, you've used SQL statements to extract the data from your data source directly. In real-world applications, it is often preferable to use a stored procedure within the data store to return the required row set. This can provide better performance, allow finer control over access permissions, and help hide the structure of the data store tables from inquisitive users.
As in traditional ADO, the .NET data access classes can work with stored procedures just as easily as they can with SQL statements. The simplest stored procedures require that you specify only the name of the procedure, and they return a set of results that can't be controlled by the ASP code used. However, stored procedures can also be written so that they accept parameters. This allows the actual content of the returned rowset to be controlled by ASP code that sets the parameter values and calls the procedure.
We've provided three examples that demonstrate the techniques for calling a stored procedure. The first example uses a simple stored procedure that does not accept parameters. The second uses a simplified inline or implicit syntax, by just adding the parameters for the stored procedure to the name of the stored procedure. The third example uses an explicit syntax by creating the parameter objects directly within the ASP code and then adding them to the Command object that executes the procedure.
Note | This last technique often turned out to be difficult in traditional ADO. It was hard to arrive at the correct data types, and often programmers resorted to using the Refresh method to create the collection of parameters with the appropriate types. The .NET Framework does not provide a direct equivalent to Refresh , but it is no longer really required anyway, as parameters of all types are now extremely easy to create (as you'll see shortly). However, there is the DeriveParameters method of the CommandBuilder object, which you look at in more detail in the next chapter. |
The Executing a Simple Stored Procedure example page ( simple-stored-proc.aspx ) shown in Figure 9-5 demonstrates how you can use a Command object to execute a stored procedure that does not require any parameters. This is often the case with stored procedures that perform some fixed operation (such as removing redundant rows, or selecting specific values like a count of products sold or the largest value in a column) on the data:
Our example uses a stored procedure named GetBooks that returns a fixed subset of rows from the BookList table “ books with the word ASP in their title. This is what the stored procedure looks like:
CREATE PROCEDURE GetBooks AS SELECT * FROM BookList WHERE Title LIKE '%ASP%'
Note | The SQL scripts we provide to create the database will also create all the stored procedures used in this chapter. |
As usual, in the example page, you're getting the connection string for the database from the web.config file, and displaying the output in an ASP DataGrid control. What's of interest here is the way that you specify the stored procedure in the Command object. As shown in the following code, the first step is to create a string that will be used as the command to be executed. In this example, it's simply the name of the stored procedure, and you display it in the page as well.
You can carry on as before when using a DataReader by creating a Connection object and a Command object. However, for maximum efficiency, indicate to the Command object that the string supplied for the first parameter of the object constructor is the name of a stored procedure. This saves SQL Server from having to check what objects with the name GetBooks are contained in the database when it executes the command.
Note | The CommandType enumeration is defined within the System.Data class library, and the possible values are StoredProcedure , TableDirect (the name of a table), and Text (the default “ a SQL statement). |
Finally, you can declare your DataReader object variable, open the connection, and execute the command. Afterwards the DataReader object you get back is bound to a DataGrid for display as usual. Also, remember to close the connection afterwards (this code is not shown here):
'create the SQL statement that will call the stored procedure Dim strCommandText As String = "GetBooks" outCommandText.InnerText = strCommandText 'and display it Dim objCommand As New OleDbCommand(strCommandText, objConnect) 'set the CommandType to 'Stored Procedure' objCommand.CommandType = CommandType.StoredProcedure 'declare a variable to hold a DataReader object Dim objDataReader As OleDbDataReader 'open the connection and execute the command objConnect.Open() objDataReader = objCommand.ExecuteReader()
Using a non-parameter stored procedure is as easy as using a SQL statement. However, it gets more complicated when the stored procedure expects you to provide parameters as well. One option is the simple in-line or implicit technique, which works fine with Microsoft SQL Server. You can use the Sql -prefixed objects (via TDS) or the OleDb -prefixed or Odbc -prefixed objects to perform the data access.
Note | This syntax might not work in all database applications (other than Microsoft SQL Server), because the in-line syntax for stored procedure parameters is not always supported by other database systems. |
The Using Implicit In-line Parameters in MS SQL Server example ( sql-stored-proc.aspx ) uses a stored procedure named FindFromTitleAndDate . This stored procedure expects two parameters to be provided, the title (or part of it) and the publication date. It returns a rowset containing all matching books. The following code shows the stored procedure code:
CREATE PROCEDURE FindFromTitleAndDate @Title varchar(50), @Date datetime AS SELECT * FROM BookList WHERE (Title LIKE @Title) AND (PublicationDate = @Date)
Figure 9-6 shows the result of running the example page:
The only real differences between the ASP code for this example and the previous one are in the command text and the use of the Sql -prefixed data access objects. The command text contains the values used for the parameters. They're hardcoded in this case, but would usually be created dynamically from a user's input:
Dim strCommandText As String = _ "FindFromTitleAndDate '%Professional%', '2002/12/01'"
However, there is one other important issue. SQL Server treats this command text as a SQL query (it automatically locates the stored procedure name within the string and parses out the parameter values). Therefore, you cannot set the CommandText property of the command object to CommandType.StoredProcedure as in the previous example “ if you do, you'll get an error saying that the stored procedure can't be found. Instead, you can either specify CommandType.Text (a SQL statement) or just omit setting the property (as was done in the example). The default is CommandType.Text .
The rest of the code, a section of which follows, functions the same as previous examples “ it creates a Connection object, a Command object, and declares a variable to hold a DataReader object. Then it opens the connection and executes the command to get the DataReader :
Dim objConnect As New SqlConnection(strConnect) Dim objCommand As New SqlCommand(strCommandText, objConnect) Dim objDataReader As SqlDataReader objConnect.Open() objDataReader = objCommand.ExecuteReader()
The rest of the code just assigns the DataReader to an ASP DataGrid as before to display the contents of the returned rows and then closes the connection to the database.
As seen in the previous example, using inline or implicit parameters when executing a stored procedure is quick and easy to program. It also provides more compact (and therefore faster) code. However, once you start using more than a couple of parameters, or need to use a return parameter to pass a result from the database to the code, the implicit technique is not really suitable. There is also the limitation that some data stores might not support it. For a more general approach, you can create each parameter for a stored procedure explicitly, and assign values to them before executing the query.
The Command objects (such as SqlCommand and OleDbCommand ) expose a Parameters collection that can contain multiple Parameter objects. Each Parameter object has a range of properties that you can access and set. When you call the ExecuteReader , ExecuteNonQuery , or ExecuteScalar method of the Command object, the parameters are sent to the data store as part of the command.
The Using Explicit Parameters with a Stored Procedure ( parameter-stored-proc.aspx ) example page demonstrates how you can use these Parameter objects. It uses a stored procedure named FindFromISBN that (given the ISBN code of a book) returns two values “ the title and the publication date. The stored procedure is as follows:
CREATE PROCEDURE FindFromISBN @ISBN varchar(12), @Title varchar(50) OUTPUT, @Date datetime OUTPUT AS SELECT @Title = Title, @Date = PublicationDate FROM BookList WHERE ISBN = @ISBN
Note that this differs in several ways from the FindFromTitleAndDate stored procedure used in the previous examples. That procedure returns a rowset containing all books that match the criteria in the two parameters. However, the FindFromISBN procedure used in this example returns the values in two OUTPUT parameters, and accepts only a single INPUT parameter. So, to get the results, you have to explicitly create the three parameters you need and feed them to the stored procedure when you execute it.
Figure 9-7 shows the example page in action. You can see that we're displaying not only the command text (the name of the stored procedure) but also the parameters that are explicitly created and added to the Command object's Parameters collection:
Much of the code is the same as that used in previous examples. The page contains <div> elements into which you insert the values seen in the screenshot. However, as there is no rowset returned from the execution of the stored procedure, you don't need a DataSet or DataReader object. Remember, all the result values are returned as parameters.
As you're specifying the parameters explicitly this time, you only need to use the stored procedure name as the CommandText property of your Command object. Then, as shown in the following code, you can create your Connection and Command objects as before, remembering this time to set the CommandType property to CommandType.StoredProcedure :
'create a string to hold the name of the stored procedure Dim strCommandText As String = "FindFromISBN" outCommandText.InnerText = strCommandText 'and display it Dim objConnect As New OleDbConnection(strConnect) Dim objCommand As New OleDbCommand(strCommandText, objConnect) objCommand.CommandType = CommandType.StoredProcedure
The syntax for creating and adding parameters to the Command object's Parameters collection is not immediately obvious. You can create a new Parameter object using the New operator, set the properties, and then pass it to the Add method of the Parameters collection. This technique is useful if you need to specify all the properties of a parameter “ there are several properties such as Direction , IsNullable , Precision , Scale , and SourceVersion that you can't set through a call to the Add method of the Parameters collection.
Note | See the Reference Class Library section of the .NET SDK for more details of the constructors and properties for the various Parameter object types such as OleDbParameter and SqlParameter . |
Alternatively, you can use a version of the Add method that creates a new parameter, adds it to the Parameters collection, and then returns a reference to it. While this technique doesn't allow you to set some of the properties of the parameter directly, you can always set these extra properties after creating and adding the parameter to the Parameters collection, using the reference to the parameter that is returned from the Add method. The following code shows how this is done in the example:
'create a variable to hold a Parameter object Dim objParam As OleDbParameter 'create a new Parameter object named 'ISBN' with the correct data 'type to match a SQL database 'varchar' field of 12 characters objParam = objCommand.Parameters.Add("ISBN", OleDbType.VarChar, 12) 'specify that it's an Input parameter and set the value objParam.Direction = ParameterDirection.Input objParam.Value = "0764543962"
Notice the three arguments to the version of the Add method used here: the name of the parameter, the data type (using the OleDbType enumeration), and the size “ in this case, 12 characters. For numeric types, you can omit the size and the default size for that data type is automatically applied. Other common data types used are Boolean , Char , DBDate , Single , Double , and Integer .
Note | There are around 40 different data types specified for the OleDbType enumeration, and around 25 for the matching SqlDbType enumeration (as used with the SqlCommand object). Search the .NET Frameworks SDK for OleDbType enumeration or SqlDbType enumeration to see the complete list. |
Once you've got a reference to the parameter, you can set the other properties. In the preceding code, specify the direction of the parameter (the options specified by the ParameterDirection enumeration are Input , Output , InputOutput , and ReturnValue ). Also specify the Value property.
Our example doesn't demonstrate all the properties that can be set for a parameter, or all the ways of creating a Parameter object.
There are several variations (or overloads) of the Add method available, ranging from the simplest one, which adds an existing Parameter object to the collection:
Parameter = Command.Parameters.Add( parameter-object )
To the most complex version that creates the Parameter object using the specified values for the properties:
Parameter = Command.Parameters.Add( parameter-name , db-type , _ size , source-column-name )
The meanings of the arguments to the Add method are as follows:
Argument | Description |
---|---|
parameter-name | The name for the parameter. |
db-type | A data type from the OleDbType , OdbcType , OracleType, or SqlDbType enumerations. |
size | The size as an integer value. |
source-column-name | Sets the SourceColumn property of the parameter “ the name of the column in a table from which the parameter value will be taken when updating the source data from a DataSet . Works in hand with the SourceVersion property. You'll see these in more detail in the next chapter. |
Note | Many of the data access examples you'll see here and in later chapters use parameter names that are not prefixed by the @ symbol when adding parameters to the ParametersCollection . This works fine when using the OleDb data access classes, because parameters are passed by position and not by name (as was the default with ADO prior to ADO.NET). In fact, any name can be used for the parameters “ the names don't have to match the parameter names in the stored procedure. However, if you use the SqlClient classes, the parameters are passed by name, and so all parameter names must be prefixed by @ in this case. |
There is also a shorter way of adding a parameter to a Command object, when all you want to specify is the parameter name and value. You can use the following syntax:
Parameter = Command.Parameters.Add("param-name", param-value)
For example:
objParam = objCommand.Parameters.Add("ISBN", "1861007035")
And if you don't need to access the new parameter in your code, you can disregard the reference returned by the Add method:
objCommand.Parameters.Add("ISBN", "1861007035")
Note | As mentioned earlier, it is possible to create parameters using the DeriveParameters method of the CommandBuilder object. However, this can only be done if you are using a DataAdapter to access the data source and not directly with a Command object. There is no direct method equivalent to the Refresh method that was available in previous versions of ADO. The CommandBuilder object is discussed in the next chapter. |
Getting back to the example code, you now need to create the two output parameters that will hold the values returned by the stored procedure. The only real difference is that you specify the direction as ParameterDirection.Output rather than ParameterDirection.Input as you did for the ISBN parameter. The following code shows how to create the parameters for the Title and Date values that will be returned from the stored procedure:
'create a new Parameter object named 'Title' with the correct data 'type to match a SQL database 'varchar' field of 50 characters 'and specify that it's an output parameter (so no value required) objParam = objCommand.Parameters.Add("Title", OleDbType.VarChar, 50) objParam.Direction = ParameterDirection.Output 'create another output Parameter object named 'Date' with the correct 'data type to match a SQL database 'datetime' field objParam = objCommand.Parameters.Add("Date", OleDbType.DBDate) objParam.Direction = ParameterDirection.Output
Now, you can display the value of the input parameter in the page, and execute the stored procedure. As shown in the following code, open the connection, call the ExecuteNonQuery method of the Command object (because you are executing a query that will not return a rowset), and close the connection again.
'display the value of the input parameter outInParams.InnerText = "ISBN='" & objCommand.Parameters("ISBN").Value & "'" 'execute the stored procedure objConnect.Open() objCommand.ExecuteNonQuery() objConnect.Close() 'collect the values of the output parameters note the use of 'the ToString() method as they will contain DBNull if there was no 'match for the ISBN and this will cause an error if displayed strTitle = objCommand.Parameters("Title").Value.ToString() strDate = objCommand.Parameters("Date").Value.ToString() outOutParams.InnerHtml = "Title='" & strTitle & "' Date=" & strDate
Afterwards, provided there was no error (although not shown here, you should include some basic error handling code in your pages), you can extract the returned values from the two output parameters and display them.