BeginExecuteNonQuery callback, stateObject This overloaded method also starts the process asynchronously, and it expects to receive an object of theAsynchCallbackinstance.. The callback
Trang 1After you close the Add Table dialog box, you see a visual representation of the table that you selected in
the Query Builder dialog box (see Figure 8-19) You can then select some or all the fields to be returned
from the query For this example, you want everything returned from both the Customers and the Orders
table, so select the first check box with the asterisk (*) Notice that the query listed in this dialog box now
saysSELECT * FROM Customers After the word ‘‘Customers,’’ add text to the query so that it looks like
the following:
SELECT Customers.* FROM Customers WHERE (CustomerID LIKE @Customer)
With this query, you specify that you want to return the customer information when theCustomerIDfits
the parameter that you pass into the query from your code (using@Customer)
After your query is in place, simply click OK and then click the Next button to have not only theselect
query, but also theinsert,update, anddeletequeries generated for you
Figure 8-20 shows you the final page after all the queries have been generated
Figure 8-20
Trang 2After you reach this point, you can either click the Previous button to return to one of the prior steps
in order to change a setting or the query itself, or you can click the Finish button to apply everything
to your TableAdapter After you are finished using the wizard, notice there is a visual representation
of theCustomersTableAdapterthat you just created (see Figure 8-21) Along with that is aDataTable
object for the Customers table TheTableAdapterand theDataTableobjects that are shown on the
design surface are also labeled with their IDs Therefore, in your code, you can address this TableAdapter that you just built by referring to it asCustomerOrdersTableAdapters.CustomersTableAdapter The
second TableAdapter that queries the Orders table is then shown and referred to as
CustomerOrders-TableAdapters.OrdersTableAdapter
Figure 8-21
After you have the two DataAdapters in place, you will also notice that there is an automatic relation put into place for you This is represented by the line between the two items on the page Right-clicking on the relation, you can edit the relation with the Relation dialog box (see Figure 8-22)
In the end, Visual Studio has taken care of a lot for you Again, this is not the only way to complete all
these tasks
Using the CustomerOrders DataSet
Now comes the fun part — building the ASP.NET that will use all the items that were just created! The goal is to allow the end user to send in a request that contains just the CustomerID In return, he will
Trang 3get back a complete DataSet containing not only the customer information, but also all the relevant
order information Listing 8-29 shows you the code to build all this functionality You need only a single
method in addition to thePage_Load: theGetCustomerOrders()method The page should be laid out as
is shown here in Figure 8-23
Figure 8-22
The page that you create should contain a single TextBox control, a Button control, and two GridView
controls (GridView1 and GridView2) The code for the page is shown in Listing 8-29
Trang 4Figure 8-23
Listing 8-29: The aspx page
<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb"
Inherits="_Default" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>CustomerOrders</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Enter Customer ID:
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<br />
Continued
Trang 5<asp:Button ID="Button1" runat="server" Text="Select" />
<br />
<br />
<asp:GridView ID="GridView1" runat="server" BackColor="White"
BorderColor="#999999" BorderStyle="Solid" BorderWidth="1px"
CellPadding="3" ForeColor="Black" GridLines="Vertical">
<FooterStyle BackColor="#CCCCCC" />
<PagerStyle BackColor="#999999" ForeColor="Black"
HorizontalAlign="Center" />
<SelectedRowStyle BackColor="#000099" Font-Bold="True"
ForeColor="White" />
<HeaderStyle BackColor="Black" Font-Bold="True" ForeColor="White" />
<AlternatingRowStyle BackColor="#CCCCCC" />
</asp:GridView>
<br />
<asp:GridView ID="GridView2" runat="server" BackColor="White"
BorderColor="#999999" BorderStyle="Solid" BorderWidth="1px"
CellPadding="3" ForeColor="Black" GridLines="Vertical">
<FooterStyle BackColor="#CCCCCC" />
<PagerStyle BackColor="#999999" ForeColor="Black"
HorizontalAlign="Center" />
<SelectedRowStyle BackColor="#000099" Font-Bold="True"
ForeColor="White" />
<HeaderStyle BackColor="Black" Font-Bold="True" ForeColor="White" />
<AlternatingRowStyle BackColor="#CCCCCC" />
</asp:GridView>
</div>
</form>
</body>
</html>
The code-behind for the page is presented in Listing 8-30
Listing 8-30: The code-behind for the CustomerOrders page
VB
Imports System
Partial Class _Default
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Me.Load
If Page.IsPostBack Then GetCustomerOrders(TextBox1.Text) End If
End Sub
Protected Sub GetCustomerOrders(ByVal custId As String)
Dim myDataSet As New CustomerOrders Dim custDA As New CustomerOrdersTableAdapters.CustomersTableAdapter Dim ordersDA As New CustomerOrdersTableAdapters.OrdersTableAdapter
Trang 6custDA.Fill(myDataSet.Customers, custId)
ordersDA.Fill(myDataSet.Orders, custId)
myDataSet.Customers(0).Phone = "NOT AVAILABLE"
myDataSet.Customers(0).Fax = "NOT AVAILABLE"
GridView1.DataSource = myDataSet.Tables("Customers")
GridView1.DataBind()
GridView2.DataSource = myDataSet.Tables("Orders")
GridView2.DataBind()
End Sub
End Class
C#
using System;
using CustomerOrdersTableAdapters;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
GetCustomerOrders(TextBox1.Text);
}
}
protected void GetCustomerOrders(string custId)
{
CustomerOrders myDataSet = new CustomerOrders();
CustomersTableAdapter custDA = new CustomersTableAdapter();
OrdersTableAdapter ordersDA = new OrdersTableAdapter();
custDA.Fill(myDataSet.Customers, custId);
ordersDA.Fill(myDataSet.Orders, custId);
myDataSet.Customers[0].Phone = "NOT AVAILABLE";
myDataSet.Customers[0].Fax = "NOT AVAILABLE";
GridView1.DataSource = myDataSet.Tables["Customers"];
GridView1.DataBind();
GridView2.DataSource = myDataSet.Tables["Orders"];
GridView2.DataBind();
}
}
Now there is not much code here One of the first things done in the method is to create an instance of
the typed DataSet In the next two lines of code, thecustDAand theordersDAobjects are used In this
case, the only accepted parameter,custId, is being set for both the DataAdapters After this parameter
is passed to the TableAdapter, this TableAdapter queries the database based upon theselectquery that you programmed into it earlier using the TableAdapter wizard
Trang 7After the query, the TableAdapter is instructed to fill the instance of the DataSet Before the DataSet
is returned to the consumer, you can change how the result is output to the client If you are
pass-ing customer information, you may want to exclude some of the information Because the DataSet is
a typed DataSet, you have programmatic access to the tables In this example, the code specifies that in
the DataSet, in the Customers table, in the first row (remember it is zero-based), make the value of the
Phone and Fax fields equal toNOT AVAILABLE
By compiling and running the ASP.NET page, you are able to test it from the test page using the
Cus-tomerID of ALFKI (the first record of the Customers table in the Northwind database) The results are
returned to you in the browser (see Figure 8-24)
Figure 8-24
Asynchronous Command Execution
When you process data using ADO or previous versions of ADO.NET, each command is executed
sequentially The code waits for each command to complete before the next one is processed When
you use a single database, the sequential processing enables you to reuse the same connection object for
all commands However, with the introduction of MARS, you can now use a single connection for
mul-tiple, concurrent database access Since the introduction of ADO.NET 2.0, ADO.NET has enabled users
to process database commands asynchronously This enables you to not only use the same connection,
but also to use it in a parallel manner The real advantage of asynchronous processing becomes apparent
Trang 8when you are accessing multiple data sources — especially when the data access queries across these
databases aren’t dependent on each other You can now open a connection to the database in an
asyn-chronous manner When you are working with multiple databases, you can now open connections to
them in a parallel fashion as well
To make this work, be sure to addAsynchronous Processing = true;to your connection string.
Asynchronous Methods of the SqlCommand Class
TheSqlCommandclass provides a few additional methods that facilitate executing commands
asyn-chronously These new methods are summarized in the following table
BeginExecute-NonQuery()
This method expects a query that doesn’t return any results and starts it asynchronously The return value is a reference to an object of the
SqlAsyncResultclass that implements theIAsyncResultinterface The returned object can be used to monitor the process as it runs and when it is completed
BeginExecuteNonQuery
(callback,
stateObject )
This overloaded method also starts the process asynchronously, and it expects to receive an object of theAsynchCallbackinstance The callback method is called after the process is finished running so that you can proceed with other tasks The second parameter receives any custom-defined object This object is passed to the callback automatically
It provides an excellent mechanism for passing parameters to the callback method The callback method can retrieve the custom-defined state object
by using theAsyncStateproperty of theIAsyncResultinterface
EndExecuteNonQuery
(asyncResult)
This method is used to access the results from theBeginExecuteNonQuery
method When calling this method, you are required to pass the same
SqlAsyncResultobject that you received when you called the
BeginExecuteNonQuerymethod This method returns an integer value containing the number of rows affected
BeginExecuteReader
This method expects a query that returns a result set and starts it asynchronously The return value is a reference to an object of
SqlAsyncResultclass that implementsIAsyncResultinterface The returned object can be used to monitor the process as it runs and as it is completed
BeginExecuteReader
(commandBehavior)
This overloaded method works the same way as the one described previously It also takes a parameter containing a command behavior enumeration just like the synchronousExecuteReadermethod
BeginExecuteReader
(callback,
stateObject)
This overloaded method starts the asynchronous process and it expects to receive an object ofAsyncCallbackinstance The callback method is called after the process finishes running so that you can proceed with other tasks The second parameter receives any custom-defined object This object is passed to the callback automatically It provides an excellent mechanism for passing parameters to the callback method The callback method can retrieve the custom-defined state object by using theAsyncStateproperty
Trang 9Method Description
BeginExecuteReader
(callback,
stateObject,
commandBehavior)
This overloaded method takes an instance of theAsyncCallbackclass and uses it to fire a callback method when the process has finished running
The second parameter receives a custom object to be passed to the callback method, and the third parameter uses the command behavior enumeration
in the same way as the synchronousExecuteReadermethod
EndExecuteReader This method is used to access the results from theBeginExecuteReader
method When calling this method, you are required to pass the same
SqlAsyncResultobject that you receive when you called the
BeginExecuteReadermethod This method returns aSqlDataReader
object containing the result of the SQL query
BeginExecute-XmlReader
This method expects a query that returns the result set as XML The return value is a reference to an object ofSqlAsyncResultclass that implements
IAsyncResultinterface The returned object can be used to monitor the process as it runs and as it is completed
BeginExecute-XmlReader (callback,
stateObject)
This overloaded method starts the asynchronous process, and it expects to receive an object ofAsyncCallbackinstance The callback method is called after the process has finished running so that you can proceed with other tasks The second parameter receives any custom-defined object This object is passed to the callback automatically It provides an excellent mechanism for passing parameters to the callback method The callback method can retrieve the custom-defined state object by using the
AsyncStateproperty of theIAsyncResultinterface
EndExecuteXmlReader This method is used to access the results from theBeginExecuteXmlReader
method When calling this method, you are required to pass the same
SqlAsyncResultobject that you received when you called the
BeginExecuteXmlReadermethod This method returns an XML Reader object containing the result of the SQL query
IAsyncResult Interface
All the asynchronous methods for theSqlCommandclass return a reference to an object that exposes the
IAsyncResultinterface The properties of this interface are shown in the following table
Property Description
AsyncState This read-only property returns an object that describes the state of the process
AsyncWaitHandle This read-only property returns an instance ofWaitHandlethat can be used to
set the time out, test whether the process has completed, and force the code to wait for completion
Completed-Synchronously
This read-only property returns a Boolean value that indicates whether the process was executed synchronously
IsCompleted This read-only property returns a Boolean value indicating whether the process
has completed
Trang 10Some of the asynchronous methods of theSqlCommandclass receive an instance of theAsyncCallback
class This class is not specific to ADO.NET and is used by many objects in the NET Framework It is
used to specify those methods that you want to execute after the asynchronous process has finished
running This class uses its constructor to receive the address of the method that you want to use for
callback purposes
WaitHandle Class
This class is an abstract class used for multiple purposes such as causing the execution to wait for any
or all asynchronous processes to finish To process more than one database command asynchronously,
you can simply create an array containing wait handles for each asynchronous process Using the static methods of theWaitHandleclass, you can cause the execution to wait for either any or all wait handles
in the array to finish processing
TheWaitHandleclass exposes a few methods, as shown in the following table
out It returns a Boolean value containingTrueif the process completed successfully andFalseif it timed out
WaitOne
(milliseconds,
exitContext)
This overloaded method receives an integer value as the first parameter
This value represents the time out in milliseconds The second parameter receives a Boolean value specifying whether the method requires asynchronous context and should be set toFalsefor asynchronous processing
WaitOne (timeSpan,
exitContext)
This overloaded method receives aTimeSpanobject to represent the time-out value The second parameter receives a Boolean value specifying whether the method requires asynchronous context and should be set to
Falsefor Asynchronous processing
WaitAny
(waitHandles)
This is a static method used if you are managing more than one
WaitHandlein the form of an array Using this method causes the execution to wait for any of the asynchronous processes that have been started and whose wait handles are in the array being passed to it The
WaitAnymethod must be called repeatedly — once for eachWaitHandle
you want to process
WaitAny (waitHandles,
milliseconds,
exitContext)
This overloaded method receives the time-out value in the form of milliseconds and a Boolean value specifying whether the method requires asynchronous context It should be set toFalsefor asynchronous
processing
WaitAny (waitHandles,
timeSpan,
exitContext
This overloaded method receives the time-out value in the form of a
TimeSpanobject The second parameter receives a Boolean value specifying whether the method requires asynchronous context It should be set to
Falsefor asynchronous processing