The code should allow for a wait until both asynchronous processes finish running before the data binds the result sets to the respective GridView controls.. TheWaitAnymethod enables you
Trang 1to a specific customer The GridView control on the bottom shows the results of executing the second
query that retrieved a list of all orders submitted by a specific customer
Figure 8-25
The code shown in Listing 8-33 reveals some of the elegance of using the wait approach However, it
is still not the most efficient code you can write with ADO.NET 2.0 The code should allow for a wait
until both asynchronous processes finish running before the data binds the result sets to the respective
GridView controls
You can change the code shown in Listing 8-33 just a little to gain even more efficiency Replace the
WaitAllmethod with theWaitAnymethod TheWaitAnymethod enables you to handle the results of
each of the asynchronous processes as soon as each is completed without waiting for other processing to
finish To use theWaitAnymethod and still manage the execution of all asynchronous processes, you can
also add a loop that enables you to make sure that all asynchronous processes are handled after they are
completed
TheWaitAnymethod returns an Integer value that indicates an array index of the wait handle that has
finished running Using this return value, you can easily find the correct wait handle and process the
result set retrieved from the query that was executed in that particular process, as shown in Listing 8-34
Trang 2Listing 8-34: Use of the WaitAny method to process multiple asynchronous processes
VB
<%@ Page Language="VB" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Configuration" %>
<script runat="server">
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim DBCon As SqlConnection
Dim OrdersCommand As SqlCommand = New SqlCommand()
Dim CustCommand As SqlCommand = New SqlCommand()
Dim OrdersReader As SqlDataReader
Dim CustReader As SqlDataReader
Dim OrdersASyncResult As IAsyncResult
Dim CustAsyncResult As IAsyncResult
Dim WHIndex As Integer
Dim WHandles(1) As Threading.WaitHandle
Dim OrdersWHandle As Threading.WaitHandle
Dim CustWHandle As Threading.WaitHandle
DBCon = New SqlConnection()
DBCon.ConnectionString = _
ConfigurationManager.ConnectionStrings("DSN_NorthWind").ConnectionString
CustCommand.CommandText = _
"SELECT * FROM Customers WHERE CompanyName = ’Alfreds Futterkiste’"
CustCommand.CommandType = CommandType.Text
CustCommand.Connection = DBCon
OrdersCommand.CommandText = _
"SELECT Customers.CompanyName, Customers.ContactName, " & _
"Orders.OrderID, Orders.OrderDate, " & _
"Orders.RequiredDate, Orders.ShippedDate " & _
"FROM Orders, Customers " & _
"WHERE Orders.CustomerID = Customers.CustomerID " & _
"AND Customers.CompanyName = ’Alfreds Futterkiste’ " & _
"ORDER BY Customers.CompanyName, Customers.ContactName"
OrdersCommand.CommandType = CommandType.Text
OrdersCommand.Connection = DBCon
’ Opening the database connection
DBCon.Open ()
Continued
Trang 3’ Retrieving customer information asynchronously CustAsyncResult = CustCommand.BeginExecuteReader()
’ Retrieving orders list asynchronously OrdersASyncResult = OrdersCommand.BeginExecuteReader() CustWHandle = CustAsyncResult.AsyncWaitHandle
OrdersWHandle = OrdersASyncResult.AsyncWaitHandle
’ Filling Wait Handles array with the two wait handles we
’ are going to use in this code WHandles(0) = CustWHandle WHandles(1) = OrdersWHandle
’ Looping 2 times because there are 2 wait handles
’ in the array For Index As Integer = 0 To 1
’ We are only waiting for any of the two
’ asynchronous process to finish running WHIndex = Threading.WaitHandle.WaitAny(WHandles)
’ The return value from the WaitAny method is
’ the array index of the Wait Handle that just
’ finsihed running Select Case WHIndex Case 0
CustReader = CustCommand.EndExecuteReader(CustAsyncResult) gvCustomers.DataSource = CustReader
gvCustomers.DataBind() Case 1
OrdersReader = _ OrdersCommand.EndExecuteReader(OrdersASyncResult) gvOrders.DataSource = OrdersReader
gvOrders.DataBind() End Select
Next
’ Closing connection DBCon.Close() End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>The Wait Any Approach</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="gvCustomers" Width="100%" runat="server"></asp:GridView>
<br /><br />
Continued
Trang 4<asp:GridView ID="gvOrders" Width="100%" AutoGenerateColumns="False"
runat="server">
<Columns>
<asp:BoundField HeaderText="Company Name"
DataField="CompanyName"></asp:BoundField>
<asp:BoundField HeaderText="Contact Name"
DataField="ContactName"></asp:BoundField>
<asp:BoundField HeaderText="Order Date" DataField="orderdate"
DataFormatString="{0:d}"></asp:BoundField>
<asp:BoundField HeaderText="Required Date" DataField="requireddate"
DataFormatString="{0:d}"></asp:BoundField>
<asp:BoundField HeaderText="Shipped Date" DataField="shippeddate"
DataFormatString="{0:d}"></asp:BoundField>
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>
C#
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Configuration" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
SqlConnection DBCon;
SqlCommand OrdersCommand = new SqlCommand();
SqlCommand CustCommand = new SqlCommand();
SqlDataReader OrdersReader;
SqlDataReader CustReader;
IAsyncResult OrdersASyncResult;
IAsyncResult CustAsyncResult;
int WHIndex;
System.Threading.WaitHandle[] WHandles =
new System.Threading.WaitHandle[1];
System.Threading.WaitHandle OrdersWHandle;
System.Threading.WaitHandle CustWHandle;
DBCon = new SqlConnection();
DBCon.ConnectionString =
ConfigurationManager.ConnectionStrings["DSN_NorthWind"].ConnectionString;
CustCommand.CommandText =
"SELECT * FROM Customers WHERE CompanyName = ’Alfreds Futterkiste’";
CustCommand.CommandType = CommandType.Text;
CustCommand.Connection = DBCon;
OrdersCommand.CommandText =
Continued
Trang 5"SELECT Customers.CompanyName, Customers.ContactName, " +
"Orders.OrderID, Orders.OrderDate, " +
"Orders.RequiredDate, Orders.ShippedDate " +
"FROM Orders, Customers " +
"WHERE Orders.CustomerID = Customers.CustomerID " +
"AND Customers.CompanyName = ’Alfreds Futterkiste’ " +
"ORDER BY Customers.CompanyName, Customers.ContactName";
OrdersCommand.CommandType = CommandType.Text;
OrdersCommand.Connection = DBCon;
// Opening the database connection DBCon.Open();
// Retrieving customer information asynchronously CustAsyncResult = CustCommand.BeginExecuteReader();
// Retrieving orders list asynchronously OrdersASyncResult = OrdersCommand.BeginExecuteReader();
CustWHandle = CustAsyncResult.AsyncWaitHandle;
OrdersWHandle = OrdersASyncResult.AsyncWaitHandle;
// Filling Wait Handles array with the two wait handles we // are going to use in this code
WHandles[0] = CustWHandle;
WHandles[1] = OrdersWHandle;
// Looping 2 times because there are 2 wait handles // in the array
for (int Index = 0; Index < 2; Index++ )
{ // We are only waiting for any of the two // asynchronous process to finish running WHIndex = System.Threading.WaitHandle.WaitAny(WHandles);
// The return value from the WaitAny method is // the array index of the Wait Handle that just // finsihed running
switch (WHIndex) {
case 0:
CustReader = CustCommand.EndExecuteReader(CustAsyncResult);
gvCustomers.DataSource = CustReader;
gvCustomers.DataBind();
break;
case 1:
OrdersReader = OrdersCommand.EndExecuteReader(OrdersASyncResult);
gvOrders.DataSource = OrdersReader;
gvOrders.DataBind();
break;
Continued
Trang 6} } // Closing connection DBCon.Close();
}
</script>
Next, look at the callback approach Using this approach, you assign a callback method to the
asyn-chronous process and use it to display the result returned by executing the SQL query
The Callback Approach
Listing 8-35 creates an inline SQL statement that retrieves the top five records from the database It
starts the asynchronous process by calling theBeginExecuteReadermethod and passing it the callback delegate No further processing is needed, and the method ends after the asynchronous process has
started After the callback method is fired, it retrieves the result and displays it on the screen
Listing 8-35: Asynchronous command processing using the callback approach
VB
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Configuration" %>
<script runat="server">
Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim DBCon As SqlConnection Dim Command As SqlCommand = New SqlCommand() Dim ASyncResult As SqlAsyncResult
DBCon = New SqlConnection() Command = New SqlCommand() DBCon.ConnectionString = _ ConfigurationManager.ConnectionStrings("DSN_NorthWind").ConnectionString
’ Selecting top 5 records from the Orders table Command.CommandText = _
"SELECT TOP 5 Customers.CompanyName, Customers.ContactName, " & _
"Orders.OrderID, Orders.OrderDate, " & _
"Orders.RequiredDate, Orders.ShippedDate " & _
"FROM Orders, Customers " & _
"WHERE Orders.CustomerID = Customers.CustomerID " & _
"ORDER BY Customers.CompanyName, Customers.ContactName"
Command.CommandType = CommandType.Text Command.Connection = DBCon
DBCon.Open()
’ Starting the asynchronous processing AsyncResult = Command.BeginExecuteReader(New _ AsyncCallback(AddressOf CBMethod), CommandBehavior.CloseConnection)
Continued
Trang 7End Sub
Public Sub CBMethod(ByVal ar As SQLAsyncResult)
Dim OrdersReader As SqlDataReader
’ Retrieving result from the asynchronous process OrdersReader = ar.EndExecuteReader(ar)
’ Displaying result on the screen gvOrders.DataSource = OrdersReader gvOrders.DataBind()
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>The Call Back Approach</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="gvOrders" Width="100%" AutoGenerateColumns="False"
runat="server">
<Columns>
<asp:BoundField HeaderText="Company Name"
DataField="CompanyName"></asp:BoundField>
<asp:BoundField HeaderText="Contact Name"
DataField="ContactName"></asp:BoundField>
<asp:BoundField HeaderText="Order Date" DataField="orderdate"
DataFormatString="{0:d}"></asp:BoundField>
<asp:BoundField HeaderText="Required Date" DataField="requireddate"
DataFormatString="{0:d}"></asp:BoundField>
<asp:BoundField HeaderText="Shipped Date" DataField="shippeddate"
DataFormatString="{0:d}"></asp:BoundField>
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>
C#
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Configuration" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
SqlConnection DBCon;
Continued
Trang 8SqlCommand Command = new SqlCommand();
SqlAsyncResult ASyncResult;
DBCon = new SqlConnection();
Command = new SqlCommand();
DBCon.ConnectionString = ConfigurationManager.ConnectionStrings["DSN_NorthWind"].ConnectionString;
// Selecting top 5 records from the Orders table Command.CommandText =
"SELECT TOP 5 Customers.CompanyName, Customers.ContactName, " +
"Orders.OrderID, Orders.OrderDate, " +
"Orders.RequiredDate, Orders.ShippedDate " +
"FROM Orders, Customers " +
"WHERE Orders.CustomerID = Customers.CustomerID " +
"ORDER BY Customers.CompanyName, Customers.ContactName";
Command.CommandType = CommandType.Text;
Command.Connection = DBCon;
DBCon.Open();
// Starting the asynchronous processing AsyncResult = Command.BeginExecuteReader(new AsyncCallback(CBMethod), CommandBehavior.CloseConnection);
}
public void CBMethod(SQLAsyncResult ar)
{
SqlDataReader OrdersReader;
// Retrieving result from the asynchronous process OrdersReader = ar.EndExecuteReader(ar);
// Displaying result on the screen gvOrders.DataSource = OrdersReader;
gvOrders.DataBind();
}
</script>
The callback approach enables you to handle the result of a command execution at a different part of
your code This feature is useful in cases where the command execution takes longer than usual and you want to respond to the user without waiting for the command execution to finish
Canceling Asynchronous Processing
The asynchronous process often takes longer than expected To alleviate this problem, you can provide
an option to the user to cancel the process without waiting for the result Canceling an asynchronous
process is as easy as calling theCancelmethod on the appropriateCommandobject This method doesn’t return any value To roll back the work that was already completed by theCommandobject, you must
provide a custom transaction to theCommandobject before executing the query You can also handle the rollback or the commit process yourself
Trang 9Asynchronous Connections
Now that you understand how to execute multiple database queries asynchronously using theCommand
object, take a quick look at how you can open database connections asynchronously, as well The
prin-ciples of working with asynchronous connections are the same as when you work with asynchronous
commands You can still use any of the three approaches you learned previously
In ADO.NET, theSqlConnectionclass exposes a couple of new properties needed when working
asyn-chronously These properties are shown in the following table
Property Description
Asynchronous This read-only property returns a Boolean value indicating whether the connection
has been opened asynchronously
indicating the state of the connection The possible values are as follows:
Summar y
In summary, ADO.NET is a powerful tool to incorporate within your ASP.NET applications ADO.NET
has a number of new technologies that provide you with data solutions that you could only dream of in
the past
Visual Studio also makes ADO.NET programming quick and easy when you use the wizards that are
available In this chapter, you saw a number of the wizards You do not have to use these wizards in
order to work with ADO.NET On the contrary, you can use some of the wizards and create the rest of
the code yourself, or you can use none of the wizards In any case, you have complete and full access to
everything that ADO.NET provides
This chapter covered a range of advanced features of ADO.NET as well These features are designed to
give you the flexibility to handle database processing in a manner never before possible with either of
the previous versions of ADO.NET or ADO
This chapter also covered the features of Multiple Active Result Sets (MARS), which enables you to
reuse a single open connection for multiple accesses to the database, even if the connection is currently
processing a result set This feature becomes even more powerful when it is used in conjunction with the
asynchronous command processing
Trang 10Quer ying with LINQ
.NET 3.5 introduces a new technology called Language Integrated Query, or LINQ (pronounced
‘‘link’’) LINQ is designed to fill the gap that exists between traditional NET languages, which offer
strong typing and full object-oriented development, and query languages such as SQL, with syntax
specifically designed for query operations With the introduction of LINQ into NET, query becomes
a first class concept in NET, whether object, XML, or data queries
LINQ includes three basic types of queries, LINQ to Objects, LINQ to XML (or XLINQ), and
LINQ to SQL (or DLINQ) Each type of query offers specific capabilities and is designed to query a
specific source
In this chapter, we look at all three flavors of LINQ, and how each enables you to simplify query
operations We also look at some new language features of the NET CLR that you will use to create
LINQ queries, as well as the tooling support added to Visual Studio to support using LINQ
LINQ to Objects
The first and most basic flavor of LINQ is LINQ to Objects LINQ to Objects enables you to perform
complex query operations against any enumerable object (any object that implements the
IEnumer-able interface) While the notion of creating enumerIEnumer-able objects that can be queried or sorted is not
new to NET, doing this in versions prior to version 3.5 usually required a significant amount of
code Often that code would end up being so complex that it would be hard for other developers to
read and understand, making it difficult to maintain
Traditional Query Methods
In order to really understand how LINQ improves your ability to query collections, you really need
to understand how this is done without it To do this, let’s take a look at how you might create a
simple query that includes a group and sort without using LINQ Listing 9-1 shows a simple Movie
class you can use as the basis of these examples