[作者]Young Joo                                          
       
     很多人问我TableAdapter是否能够从存储过程里读取多组数据结果。最直接的回答是:不能。你不能通过TableAdapter.Fill()方法来得到一个Dataset。但是我们可以通过另一种简单的方法来实现。
 
DataAdapter.Fill()和多组数据结果
 
     TableAdapter.Fill()方法通过调用DataAdapter.Fill()从数据库中读取数据。DataSet.Fill() 方法可以从存储过程里读取多组数据结果。为了获得多组数据结果,可以应用DataAdapter.Fill()的一个重载方法,它将Dataset作为参数,这样就可以把存储过程的多组数据结果返回给包含有多个表的Dataset
    这里,我们通过一个简单的示例来演示一下这种方法是怎样实现的:
    假设在Northwind数据库里有一个存储过程dbo.spSelectCustomersOrders
    CREATE PROCEDURE spSelectCustomersOrders
    AS
    BEGIN 
       SET NOCOUNT ON
       SELECT * FROM Customers 
       SELECT * FROM Orders
    END
   GO
     下面的代码调用了这个存储过程,并且把2组数据结果存储在Dataset里。
    Dim myConn As New System.Data.SqlClient.SqlConnection
    Dim myAdapter As New System.Data.SqlClient.SqlDataAdapter
    Dim mySelectCommand As New System.Data.SqlClient.SqlCommand
    Dim myDataset As New System.Data.DataSet

    myConn.ConnectionString = "Data Source=.\SQLExpress;Initial Catalog=Northwind;Integrated       Security=True"
    mySelectCommand.Connection = myConn
    mySelectCommand.CommandText = "dbo.spSelectCustomersOrders"
    myAdapter.SelectCommand = mySelectCommand
    myAdapter.Fill(myDataset)

    For Each table As System.Data.DataTable In myDataset.Tables
       Console.WriteLine("Table Name:" & table.TableName)
    Next
    代码的输出形式如下:
     Table Name: Table
     Table Name: Table1
    
     我们可以看到,DataAdapter.Fill()方法执行了存储过程,并且把2组数据结果分别存储在2个数据表里。
 
TableAdapter的解决方案
 
     然而,为什么TableAdapter.Fill()方法不能够正确地处理多组数据结果?那是因为TableAdapter.Fill()调用的DataAdapter.Fill()方法是以DataTable作为参数,而不是Dataset。这种情况,我们只需要在TableAdapter里创建一个新的Fill方法,令其调用以Dataset为参数的DataAdapter.Fill()方法。
     假设这里有一个包含CustomersOrdersNorthwindDataset.xsd文件。让我们用上面的存储过程来实现新的Fill方法。把下面的代码加到partial class文件里。(在Dataset Designer上,可以通过双击或者右键选择"View Code"来进入partial class,当然也可以手动创建一个空的class文件。)
    Namespace NorthwindDataSetTableAdapters
        Partial Public Class CustomersTableAdapter
            Public Function FillCustomersOrders(ByVal dataSet As NorthwindDataSet) As Integer 
                 Dim
multiSelectCommand As New System.Data.SqlClient.SqlCommand 
                 Dim returnValue As Integer 

                 
multiSelectCommand.Connection = Me.Connection 
                 multiSelectCommand.CommandText = "dbo.spSelectCustomersOrders" 
                 Me
.Adapter.SelectCommand = multiSelectCommand 
                 '' Map auto-created Table1 that holds the second result-set (Orders rows) to 
                 '' Orders DataTable in our Dataset. 
                 Me.Adapter.TableMappings.Add("Table1", "Orders"
                returnValue = Me.Adapter.Fill(dataSet) 

                Return
returnValue 
            End Function 
         End
Class
     End
Namespace
      有两点需要特别注意:
      首先,新的FillCustomersOrders是以Dataset为参数,这样当我们调用DataAdapter.Fill()方法时,数据结果就会准确地存储到Dataset里。
      第二,注意我们是怎样应用TableMapping将自动生成的数据表映射到Dataset里的Orders表。当应用DataAdapter.Fill()来读取多组数据结果,每一组数据结果都被单独地存储在Dataset的数据表里。默认情况下,这些数据表被命名为Table, Table1, Table2…,为了将这些数据标与Dataset里定义的数据表相对应,我们应用TableMapping。如果你打开NorthwindDataset.xsd后面的代码,在TableAdapter classInitAdapter()方法,你就会看到类似的代码:
    tableMapping.SourceTable = "Table"
    tableMapping.DataSetTable = "Customers"
    '' Colum mapping code skipped
     ...
     Me._adapter.TableMappings.Add(tableMapping)
 
      这段代码是为了保证DataAdapter.Fill方法返回的数据表与Dataset里的数据表相对应。在我们FillCustomersOrders示例里,第二组结果包含的是Orders信息,所以我们在Table1Orders之间创建了映射关系,确保数据FillOrders表中。
     把以上代码添加到partial class后,你就可以调用FillCustomersOrders方法来fill CustomersOrders
     CustomersTableAdapter.FillCustomersOrders(Me.NorthwindDataSet)
 
性能的考虑
 
      有些情况下,这种方法的确很有效。但是这也要看情况,也许你会想到这个方法可以避免多次访问数据库,从而提高性能,但如果仅仅只需要获取一小部分数据,却应用这种方法一次读取了大量的数据,这同样也会降低性能,倒不如一次读取小部分数据,需要其它数据时,与数据库建立另一个连接,再读取。ADO.NET在处理多个数据库连接方面性能优化得还是不错的,很多情况下,都不至于导致性能瓶颈。总之我们只需要遵循最基本的原则:只在需要的时候,才去读取数据。
     但有些情况下,读取多组数据结果还是很有帮助的,所以,应用我在这里所介绍的方法吧,但时刻也不要忘记性能的问题。
 
相关资源