目录
3.BindGrid方法定义一个BindGridDelegate委托
5. 读取SqlDataReader数据动态创建DataTable
在ADO.NET中,还允许Command对象异步执行它们的命令。对于很多应用程序,特别是Windows窗体应用程序来说,使用这个功能可以获得很大的性能提升,尤其在执行需要长时间运行的SQL语句时非常有效。
SqICommand对象提供了3个不同的异步调用方法:BeginExecuteReader、BeginExecuteNonQuery和BeginExecuteXmIReader。每一个方法都有其对应的“结束”方法,包括EndExecuteReader、EndExecuteNonQucry和EndExecuteXmIReader。
1.启动异步调用
Private Sub testAsynchronous()
Dim sqlStr As String = "usp_Long_Running_Procedure"
Dim conn As SqlConnection = GetConnection()
conn.ConnectionString &= "; Asynchronous Processing=true;" '异步调用
Dim comm As New SqlCommand(sqlStr, conn)
comm.CommandType = CommandType.StoredProcedure
conn.Open()
comm.BeginExecuteReader(AddressOf Me.AsyncCallback, comm, CommandBehavior.CloseConnection)
End Sub
- 调用GetPubsConnection辅助方法,获得了一个对pubs数据库的连接口
Private Function GetConnection() As SqlConnection
Return New SqlConnection(My.Settings.pubsConnectionString)
End Function
- 把"; Asynchronous Processing=true;"语句附加到Connection对象的连接字符串后面。这么做是为了让ADO.NET能够对SQL Server进行异步调用。
- 设置好连接字符串后,创建SqICommand对象并把它初始化为可以执行usp Long Running_Procedure存储过程。这个存储过程通过SQL Server中的WAITFOR DELAY语句产生20秒的延迟,然后调用usp_Get_All存储过程。usp_Get_All存储过程可以选择作者表中的所有作者。这里加入延迟的目的是为了说明在上面的存储过程运行时,Windows窗体应用程序可以执行其他任务。下面是usp_Long-Running_Procedure和usp_Get_All存储过程的SQL代码:
ALTER PROCEDURE usp_Long_Running_Procedure
AS
SET NOCOUNT ON
WAITFOR DELAY '00:00:5'
EXEC usp_Get_All
ALTER PROCEDURE usp_Get_All
AS
SELECT
au_id,au_lname,au_fname,phone,address,city,state,zip,contract
From
authors
- 最后一行代码调用了BeginExecuteReader方法。在这个调用中,首先传送一个System.AsyncCallback委托类型的委托方法(Me.AsyncCallback),在异步方法运行结束时,.NET Framework会使用这个委托返回。另外还传入了已经初始化的SqICommand对象,用于执行存储过程。最后一个参数是DataReader的CommandBehavior值,这里为commandBehavior.CloseConnection,其含义是在DataReader关闭时,自动关闭数据库的连接。
2.AsyncCallback方法:
Private Sub AsyncCallback(ByVal ar As IAsyncResult)
Dim comm As SqlCommand = CType(ar.AsyncState, SqlCommand)
Dim reader As SqlDataReader = comm.EndExecuteReader(ar)
Dim table As DataTable = GetTableFromReader(reader, "Authors")
Me.Invoke(New BindGridDelegate(AddressOf Me.BindGrid), New Object() {table})
End Sub
- 第一行代码从传入的IAsyncResult对象的AsyncState属性中获得了SqICommand对象。前面调用BeginExecuteReader方法时,传入了自己的SqlCommand对象,以便在下一行调用EndExecuteReader方法。这个方法返回了SqIDataReader对象.
- 第二行代码将SqIDataReader对象转换为DataTable类型(在后面讨论DataSct时将介绍转换的概念).
- 最后一行非常重要。如果尝试在这里把DataTable对象绑定到网格,就会出现错误,因为当前执行的线程不是主Windows线程,而用于进行数据绑定的辅助方法BindGrid只能在Windows主线程环境内调用。为了把数据返回给主Windows线程,必须使用Form对象的Invoke方法将数据编组。 Invoke方法需要两个参数,希望调用的方法委托和方法用到的所有参数。
3.BindGrid方法定义一个BindGridDelegate委托
Private Delegate Sub BindGridDelegate(ByVal table As DataTable)
Private Sub BindGrid(ByVal table As DataTable)
Me.DataGridView1.DataSource = Nothing
Me.DataGridView1.DataSource = table
End Sub
4.窗体的Invoke方法调用
Me.Invoke(New BindGridDelegate(AddressOf Me.BindGrid), New Object() {table})
这行代码传入了一个新创建的BindGridDelegate委托实例,并用指向BindGrid方法的指针对其进行初始化。这样,正在执行查询的.NET工作线程就可以安全地与主Windows线程协同工作了。
5. 读取SqlDataReader数据动态创建DataTable
Private Function GetTableFromReader(ByVal reader As SqlDataReader, ByVal str As String) As DataTable
Dim comm As SqlCommand = New SqlCommand("usp_Get_All", GetConnection)
comm.CommandType = CommandType.StoredProcedure
'Dim mReader As SqlDataReader
comm.Connection.Open()
reader = comm.ExecuteReader(CommandBehavior.CloseConnection)
Dim dt As DataTable = New DataTable
dt.TableName = str
Dim schemaTable As DataTable = reader.GetSchemaTable()
'动态构建表,添加列
For Each dr As DataRow In schemaTable.Rows
Dim dc As DataColumn = New DataColumn
'设置列的数据类型()
dc.DataType = dr(0).GetType()
'设置列的名称()
dc.ColumnName = dr(0).ToString()
'将该列添加进构造的表中
dt.Columns.Add(dc)
Next
'读取数据添加进表中
While reader.Read()
Dim row As DataRow = dt.NewRow()
'填充一行数据
For i = 0 To schemaTable.Rows.Count - 1
row(i) = reader(i).ToString()
Next
dt.Rows.Add(row)
row = Nothing
End While
reader.Close()
schemaTable = Nothing
Return dt
End Function