使用 .NET 构建分布式应用程序

Priya Dhawan

Microsoft Developer Network

修订日期:2002 年 2 月

摘要: 本文涉及使用 ADO .NET 对分层的行集执行的数据操作。(24 页打印页)

下载 Bdadotnet.msibdadotnet_beta2.msi

本页内容
简介 简介
读取操作 读取操作
使用 ADO .NET DataReader 对象 使用 ADO .NET DataReader 对象
使用 ADO .NET SqlCommand 对象和<br />
				XmlReader 使用 ADO .NET SqlCommand 对象和 XmlReader
使用 ADO .NET DataSet 对象 使用 ADO .NET DataSet 对象
写入操作 写入操作
使用 ADO .NET DataAdapter 对象 使用 ADO .NET DataAdapter 对象
插入 插入
更新 更新
删除 删除
小结 小结


简介

本文说明用来从数据源读取和写入分层行数据的技术。本文涉及的代码示例使用 SQL 托管提供程序连接到 Microsoft? SQL Server? 数据库或 Microsoft? 桌面引擎 (MSDE)。如果要连接到其他符合 OLEDB 的数据源,应当使用 ADO 托管提供程序。

ADO .NET 提供的 DataReader“数据集” 对象可用来访问从数据源返回的分层行数据。DataReader 对象旨在提供简单、快速、只读的数据访问。DataReader 对象可以访问分层行,这些行可作为与多个 SELECT 语句相对应的多个结果返回,也可以从 SQL Server 2000 以 XML 形式返回。DataReader 对象允许进行只进式读取,并在应用程序读取数据时保持与数据库的连接。

与之相比,“数据集” 对象代表数据的断开连接的、内存中的关系缓存。它可以包含多个表,这些表可被单独处理,也可以被视为相关表。“数据集” 对象具有可简化相关数据的导航和修改的功能。

使用 SQL Server .NET 数据提供程序,还可以借助于 SQLCommand 对象,通过公开名为 ExecuteXmlReader 的流 API 直接从 SQL Server 2000 获得 XML 流。ExecuteXmlReader 方法针对 SQL 连接执行 SQL 查询,并从 SQL 查询返回的 XML 构建 XmlReader 对象。ExecuteXmlReader 只能用于可返回 XML 数据的语句;如果查询通过使用 FOR XML 子句,以 XML 形式返回 SELECT 语句的结果,则它们可与 ExecuteXmlReader 一起有效地使用。

返回页首返回页首


读取操作

分层的行数据通常构造为多个相关行的集合。通常,在到数据库的单个行程中检索多个行集比对于每个行集都往返检索效率更高。这通常是通过执行一批 SQL 表达式或者一个包括多个 “选择” 语句的存储过程来完成的。分层的行还可以使用 FOR XML “选择” 语句,从 SQL Server 2000 以 XML 形式返回。

返回页首返回页首


使用 ADO .NET DataReader 对象

在本例中,批量执行两个 SQL “选择” 语句,并且通过 DataReader 对象检索两个相应的结果集。DataReader 对象允许进行只进式导航,以便数据将有可能传输到另一个容器,从而执行后来的工作。因为 DataReader 对象维护开放式数据库连接,所以从可伸缩性的角度看,快速执行传输操作并尽可能快地释放数据库连接会大有裨益。在分布式应用程序的层之间,不可能传递 DataReader 对象。

DataReader 对象的 NextResult“读取” 方法用于迭代通过结果集所在的行:

Dim sqlCmd As SQLCommand
Dim sqlDataRdr As SQLDataReader
Dim fld As Integer
Dim rptLine As String
Try
   ' Prepare the command object to execute a stored procedure returning
   ' Orders and OrderDetails
   sqlCmd = New SQLCommand()
   With sqlCmd
      .CommandType = CommandType.StoredProcedure
      .CommandText = "GetOrders"
      .Connection = New SqlConnection(myConnString)
   End With
   ' Open the Connections
   sqlCmd.Connection.Open()
   ' Execute the command specifying the DataReader to receive the results
   sqlDataRdr = sqlCmd.ExecuteReader()
   ' Iterate through the rows in the the first result set
   Do
      While (sqlDataRdr.Read)
         ' Do something with the Row   here
         rptLine = ""
         For fld = 0 To sqlDataRdr.FieldCount - 1
            rptLine = rptLine and sqlDataRdr.Item(fld).ToString
            If fld < sqlDataRdr.FieldCount - 1 Then
               rptLine = rptLine and ", "
            End If
         Next
      End While
      ' Advance the pointer to the next result set
      If Not (sqlDataRdr.NextResult) Then
         Exit Do
      End If
   Loop
Catch E As Exception
Finally
   ' Close the DataReader
   sqlDataRdr.Close()
End Try

注意 请参阅 BDAdotNetData4.vb 示例代码中的例 1(有关下载信息,请参阅本文的顶部)。

返回页首返回页首


使用 ADO .NET SqlCommand 对象和 XmlReader

Microsoft SQL Server 2000 天生支持 XML 功能。现在,“选择” 语句的结果可通过使用 FOR XML 子句以 XML 形式返回。要直接从 SQL Server 2000 检索 XML 形式的结果,可以使用 SQLCommand 对象的 ExecuteXmlReader 方法。ExecuteXmlReader 返回一个 System.Xml.XmlReader 对象,该对象包含从 SQL Server 2000 返回的 XML。

在下例中,针对 SQLCommand 对象调用 ExecuteXmlReader 方法以执行一个存储过程,该存储过程使用 FOR XML 子句返回 XML 形式的结果。

Dim sqlConn As SqlConnection
Dim sqlCmd As SqlCommand
Dim xmlRdr As XmlReader
Try
   ' Create a new connection object
   sqlConn = New SqlConnection(myConnString)
   ' Create a new command object
   sqlCmd = New SqlCommand()
   ' Specify the command to be exceuted
   With sqlCmd
      .CommandType = CommandType.StoredProcedure
      .CommandText = "GetOrdersXML"
      .Connection = sqlConn
   End With
   ' Open the connection
   sqlConn.Open()
   ' Execute the command and retrieve the row in the DataReader
   xmlRdr = sqlCmd.ExecuteXmlReader()
   ' Move to the Root Element
   xmlRdr.MoveToContent()
   ' Do something with data
   outXML = xmlRdr.ReadOuterXml
   ' Move to the Next Element
   xmlRdr.MoveToElement()
   ' Read the OrderId Attribute of the Current Element
   xmlRdr.MoveToAttribute("OrderId")
   ???…
Catch e As Exception
   ' Catch Exception here
Finally
    sqlConn.Close()
End Try

注意 请参阅 BDAdotNetData4.vb 示例代码中的例 2(有关下载信息,请参阅本文的顶部)。

返回页首返回页首


使用 ADO .NET DataSet 对象

DataAdapter 对象从数据源检索数据并填充 “数据集” 内的 “数据表” 对象。DataAdapter 需要 “连接” 对象针对数据源执行查询。

如果查询返回多个结果集,则 “数据集” 将其中的每个结果集都存储在一个单独的表中。这些表可以相互关联。

表之间的关系

在通过关系将 “数据集” 中的两个表链接起来后,使用 “数据关系” 对象,可以简化从一个表到另一个表的导航,还可以简化如下过程:对于一个 “数据表” 中的父行,从链接的另一个 “数据表” 检索所有相氐?DataRows 或者子 DataRowsDataRow 对象的 GetChildRows 重载方法用于检索子行。

可通过创建一个 “数据关系” 对象来建立表之间的关系,该对象将一个 “数据表” 中的行与另一个表中的行相关联。“数据集” 对象包含由 DataRelationCollection 对象包括的关系。

“数据集” 对象中关联表不是强制的。它们可以保持不关联。如果两个表具有某种父/子关系(如外键关系),则将这两个表进行关联会便于从一个 “数据表” 中的父行到达另一个 “数据表” 中的子行。

下面的代码示例从数据库中的 Orders 和 OrderDetails 表检索定单及其详细信息。“数据集” 对象包含与这两个数据库表相对应的 Orders 和 Details 表。“OrderId”列在这两个 “数据表” 中都出现,该列充当这两个表之间的链接:

Dim sqlDA As SqlDataAdapter
Dim hierDS As DataSet
Dim orderRow As DataRow
Dim detailRow As DataRow
Dim detailRows() As DataRow
Dim i As Integer
Try
   ' Create a new DataAdapter object
   sqlDA = New SqlDataAdapter()
   ' Create a new DataSet
   hierDS = New DataSet()
   ' Set the Table Mappings
   sqlDA.TableMappings.Add("Orders", "Orders")
   sqlDA.TableMappings.Add("Orders1", "OrderDetails")
   With sqlDA
      ' Add a SelectCommand object
      .SelectCommand = New SqlCommand()
      ' Specify the Select Command
      With .SelectCommand
          .CommandType = CommandType.StoredProcedure
          .CommandText = "GetOrders"
          .Connection = New SqlConnection(myConnString)
      End With
      ' Populate the DataSet with the returned data
      .Fill(hierDS, "Orders")
   End With

因为在调用 “填充” 方法之前,“数据集” 中没有表,所以 SQLDataAdapter 对象将自动为 “数据集” 对象创建表并用返回的数据填充它们。如果您在执行 “填充” 方法之前构建表,SQLDataAdapter 对象将只是填充现有的表:

   ' Set the Primary Key for the Tables
   hierDS.Tables("Orders").PrimaryKey = New DataColumn() _
{ hierDS.Tables("Orders").Columns("OrderId")}
   hierDS.Tables("OrderDetails").PrimaryKey = New DataColumn() _
{ hierDS.Tables("OrderDetails").Columns("OrderDetailId")}
   ' Establish the Foreign Key relationship between the tables
   hierDS.Relations.Add("Order_Detail", _
                    hierDS.Tables("Orders").Columns("OrderId"), _
                    hierDS.Tables("OrderDetails").Columns("OrderId"))
   ' Get an order
   orderRow = hierDS.Tables("Orders").Rows(0)
   ' Retrieve child rows for the order
   detailRows = orderRow.GetChildRows("Order_Detail")
   ' Do something with the child rows collection
   For i = 0 To detailRows.Length - 1
      detailRow = detailRows(i)
      ' Do something with the detail row
      strDetail = detailRow("OrderId").ToString & ", " & _
            detailRow("OrderDetailId").ToString & ", " & _
detailRow("ItemId").ToString & ", " & _
detailRow("UnitPrice").ToString & ", " &
detailRow("Quantity").ToString)
   Next
Catch E As Exception
   ' Catch Exception here
Finally
   ' Close connection and other Cleanup code here
End Try

注意 请参阅 BDAdotNetData4.vb 示例代码中的例 3(有关下载信息,请参阅本文的顶部)。

返回页首返回页首


写入操作

如果要提交在分层数据中进行的更改,而且该数据中包含多个从两个或多个相关表返回的结果集,则需要维护数据完整性。例如,引用完整性表示任何引用表中的外键都必须始终引用被引用表中的有效行。因此,如果被引用表中的父行正被引用表引用,它就无法删除。同样,如果相应的行在被引用表中不存在,就无法在引用表中插入它们。

因为 ADO .NET “数据集” 对象允许在数据库中进行检索、操作和更新数据,所以它确保两个表之间的关系在插入、更新和删除过程中保持一致。它还允许在维护数据完整性的同时执行级联更新和删除。

返回页首返回页首


使用 ADO .NET DataAdapter 对象

DataAdapter“更新” 方法将缓存在 “数据集” 对象中的更改提交到数据源。DataAdapter 使用 InsertCommand 提交新行,使用 UpdateCommand 提交经过修改的行,使用 DeleteCommand 从数据库中删除行。当您调用 “更新” 方法时,DataAdapter 检查经过修改的行,并确定要执行哪个 “命令” 对象,以便提交该行的待定更改。

在调用 DataAdapter“更新” 方法之前,必须设置 InsertCommandUpdateCommandDeleteCommand 属性,具体情况取决于对 “数据集” 中的数据进行了的更改。例如,如果从 “数据集” 中删除了行,则必须设置 DeleteCommand 属性。可以使用 CommandBuilder 对象自动生成 “插入”“更新”“删除” 命令如果您指定 DataAdapterInsertCommandUpdateCommandDeleteCommand 属性,则 “更新” 方法对于在 “数据集” 中插入、更新或删除的每一行分别执行 Insert、Update 或 Delete 命令。否则,CommandBuilder 基于 DataAdapter 的 SelectCommand 属性,生成与数据库协调更改所必需的 SQL 命令。因此,必须设置 DataAdapterSelectCommand 属性,以便 CommandBuilder 生成 “插入”“更新”“删除” 命令。

首选的方法是完全指定自己的 InsertCommandDeleteCommandUpdateCommand,以便显式控制如何完成更新并且获得比“自动生成”情况更好的性能。当您自己的代码通过在每个行程中处理多行操作来减少到数据源的往返行程时,尤其如此。

可以为 DataAdapterInsertCommandUpdateCommandDeleteCommand 属性指定参数化查询或存储过程。参数化查询或存储过程中的参数与 DataTable 对象中的列相对应。因此,DataAdapter 对象支持针对数据库中的单个表执行更新。所以,在将所做的更改提交到数据库时,将要求 “数据集” 对象中的每个表都有一个单独的 DataAdapter 对象。

返回页首返回页首


插入

如果新行与父表中有效的(或者已经存在的)行相对应,则可以将它们添加到子表中。如果某一行引用父表中无效的行,就不应当将它添加到子表中。

首先,应当将新行插入到父表中,然后只应当将相应的子行添加到子表中。出于相同的原因,在与数据库协调所做的更改时,应当首先针对与父表相对应的 DataAdapter 对象调用 “更新” 方法。

自动生成的 Insert 命令

自动生成的 Insert 有一个问题,那就是 Identity 列的主键 Id 不返回到 “数据集”。为了解决此问题,我们将使用一个存储过程,该存储过程返回父的主键并允许对于子行使用自动生成的 Insert。在下面的代码示例中,我们创建两个 DataAdapter 对象,以便填充单个 “数据集” 对象中的两个表。我们在 “数据集” 对象中的两个表之间定义关系,并在这两个表中插入新行。首先针对与父表(在本例中为 Orders)相对应的 DataAdapter 对象调用 “更新” 方法,以便将所做的更改提交到数据库。在此之后,针对与子表(在本例中为 OrderDetails)相对应的 DataAdapter 对象调用 “更新” 方法:

Dim sqlConn As SQLConnection
Dim sqlDAOrder As SqlDataAdapter
Dim sqlDADetail As SqlDataAdapter
Dim hierDS As DataSet
Dim sqlCmdBldrDetail As SqlCommandBuilder
Dim orderRow As DataRow
Dim detailRow As DataRow
Try
   ' Create a new SQLConnection object
   sqlConn = New SqlConnection(Common.getConnectionString)
   ' Create a new SQLDataAdapter object for the Order table
   sqlDAOrder = New SqlDataAdapter()
   ' Create a new SqlDataAdapter object for the OrderDetails table
   sqlDADetail = New SqlDataAdapter()
   ' Create a new DataSet
   hierDS = New DataSet()
   ' Create a new SQLCommandBuilder object to automatically generate
   ' Update Statements
   sqlCmdBldrDetail = New SqlCommandBuilder(sqlDADetail)
   With sqlDAOrder
      ' Add a SelectCommand object
      .SelectCommand = New SqlCommand()
      ' Specify the Select Command
      With .SelectCommand
         .CommandType = CommandType.Text
         .CommandText = "Exec GetOrderHeader @OrderId=-1"
         .Connection = sqlConn
      End With
     ' Add a InsertCommand object
      .InsertCommand = New SqlCommand()
      ' Specify the Insert Command
      With .InsertCommand
         .CommandType = CommandType.StoredProcedure
         .CommandText = "InsertOrderHeader"
         .Connection = sqlConn
         ' Define parameters in the parameterized Insert statement
         .Parameters.Add _
               (New SqlParameter("@CustomerId", SqlDbType.Int))
         ' Set Direction property
         .Parameters("@CustomerId").Direction = ParameterDirection.Input
         ' Set the SourceColumn property
         .Parameters("@CustomerId").SourceColumn = "CustomerId"
         .Parameters.Add _
               (New SqlParameter("@OrderDate", SqlDbType.DateTime))
         ' Set Direction property
         .Parameters("@OrderDate").Direction = ParameterDirection.Input
         ' Set the SourceColumn property
         .Parameters("@OrderDate").SourceColumn = "OrderDate"
         ???…
         ???…
         .Parameters.Add _
               (New SqlParameter("@OrderId", SqlDbType.Int))
         ' Set Direction property
         .Parameters("@OrderId").Direction = ParameterDirection.Output
         ' Set the SourceColumn property
         .Parameters("@OrderId").SourceColumn = "OrderId"
      End With
      ' Populate the Orders table
      .Fill(hierDS, "Orders")
   End With
   With sqlDADetail
      ' Add a SelectCommand object
      .SelectCommand = New SqlCommand()
      ' Specify the Select Command
      With .SelectCommand
         .CommandType = CommandType.Text
         .CommandText = "Exec GetOrderDetails @OrderId=-1"
         .Connection = sqlConn
      End With
      ' Populate the Details table
      .Fill(hierDS, "Details")
   End With
   ' Establish a relationship between the tables
   hierDS.Relations.Add("Order_Detail", _
hierDS.Tables("Orders").Columns("OrderId"), _
hierDS.Tables("Details").Columns("OrderId"))
   ' Create a new row for the Orders table
   orderRow = hierDS.Tables("Orders").NewRow()
   ' Set the value of each of the column present in the Orders table
   orderRow.Item("CustomerId") = 1
   orderRow.Item("OrderStatus") = 400
   orderRow.Item("OrderDate") = Now()
   ???…
   ' Add the row to the DataSet
   hierDS.Tables("Orders").Rows.Add(orderRow)
   ' Reconcile changes with the data source
   sqlDAOrder.Update(hierDS, "Orders")
   ' Create a new Row for the Details table
   detailRow = hierDS.Tables("Details").NewRow()
   detailRow.Item("OrderId") = orderRow.Item("OrderId")
   detailRow.Item("ItemId") = 13
   ???…
   ' Add the row to the DataSet
   hierDS.Tables("Details").Rows.Add(detailRow)
   ' Reconcile changes with the data source
   sqlDADetail.Update(hierDS, "Details")
Catch e As Exception
   ' Catch Exception here
Finally
   ' Cleanup code
End Try

注意 请参阅 BDAdotNetData4.vb 示例代码中的例 4(有关下载信息,请参阅本文的顶部)。

请注意,如果添加到子表中的新行与添加到父表中的新行相对应,就应当首先针对父表执行更新,以便维护数据的完整性。子行必须始终引用父表中的有效行。

使用 InsertCommand 属性

在本例中,定单标题和详细信息以 XML 形式传入存储过程,以便事务可在到数据库的单个往返行程中完成。

要指定自己的 “插入” 语句,并且应当在针对 DataAdapter 调用 “更新” 方法时执行此语句,请设置 InsertCommand 属性。可对参数化查询或存储过程设置 InsertCommand 属性。可以按照定义 “命令” 对象的参数时使用的方法,定义 InsertCommand 的参数。SQL 托管提供程序允许使用命名参数。

您需要设置所有参数的 SourceColumn 属性。SourceColumn 告诉 DataAdapter 对象,表中的哪一列将提供它的值。将它设置为实际的参数名:

Dim sqlConn As SqlConnection
Dim sqlDAOrder As SqlDataAdapter
Dim sqlDADetail As SqlDataAdapter
Dim hierDS As DataSet
Try
   ' Create a new connection
   sqlConn = New SqlConnection(Common.getConnectionString)
   ' Create a new SqlDataAdapter object for the Order table
   sqlDAOrder = New SqlDataAdapter()
   ' Create a new SqlDataAdapter object for the OrderDetails table
   sqlDADetail = New SqlDataAdapter()
   ' Create a new DataSet
   hierDS = New DataSet()
   With sqlDAOrder
      ' Add a SelectCommand object
      .SelectCommand = New SqlCommand()
      ' Specify the Select command
      With .SelectCommand
         .CommandType = CommandType.Text
         .CommandText = "Exec GetOrderHeader @OrderId=-1"
         .Connection = sqlConn
      End With
      ' Add a InsertCommand object
       .InsertCommand = New SqlCommand()
       ' Specify the Insert Command
      With .InsertCommand
         .CommandType = CommandType.StoredProcedure
         .CommandText = "InsertOrder"
         .Connection = sqlConn
         ' Define parameters in the parameterized Insert statement
         .Parameters.Add _
             (New SqlParameter("@Order", SqlDbType.NVarChar, 4000))
         ' Set Direction property
         .Parameters("@Order").Direction = ParameterDirection.Input
         .Parameters.Add _
               (New SqlParameter("@OrderId", SqlDbType.Int))
         ' Set Direction property
         .Parameters("@OrderId").Direction = ParameterDirection.Output
         ' Set the SourceColumn property
         .Parameters("@OrderId").SourceColumn = "OrderId"
      End With
      ' Populate the DataSet with the returned data
      .Fill(hierDS, "Orders")
   End With
   With sqlDADetail
      ' Add a SelectCommand object
      .SelectCommand = New SqlCommand()
      ' Specify the Select command for the sqlDADetail object
      With .SelectCommand
         .CommandType = CommandType.Text
         .CommandText = "Exec GetOrderDetails @OrderId=-1"
        .Connection = sqlConn
      End With
      ' Populate the DataSet with the returned data
      .Fill(hierDS, "Details")
   End With
   ' Establish the Foreign Key relationship between the tables
    hierDS.Relations.Add("Order_Detail", _
hierDS.Tables("Orders").Columns("OrderId"), _
hierDS.Tables("Details").Columns("OrderId"))
   ' Create a new row for the Orders table
   orderRow = hierDS.Tables("Orders").NewRow()
   ' Set the value of each of the column present in the Orders table
   orderRow.Item("OrderId") = -1
   orderRow.Item("CustomerId") = 1
   orderRow.Item("OrderStatus") = 400
   ???…
   ' Add the row to the DataSet
   hierDataSet.Tables("Orders").Rows.Add(orderRow)
   ' Create a new Row for the Details table
   detailRow = hierDataSet.Tables("Details").NewRow()
   detailRow.Item("OrderId") = orderRow.Item("OrderId")
   detailRow.Item("ItemId") = 13
   ???…
   ' Add the row to the DataSet
   hierDataSet.Tables("Details").Rows.Add(detailRow)
   ' Create a new Row for the Details table
   detailRow = hierDataSet.Tables("Details").NewRow()
   detailRow.Item("OrderId") = orderRow.Item("OrderId")
   detailRow.Item("ItemId") = 12
   ???…
   ' Add the row to the DataSet
   hierDataSet.Tables("Details").Rows.Add(detailRow)
   sqlDAOrder.InsertCommand.Parameters("@Order").Value = _
    hierDataSet.GetXml()
   ' Reconcile changes with the data source
   sqlDAOrder.Update(hierDataSet, "Orders")
Catch E As Exception
Finally
    ' Cleanup code here
End Try

注意 请参阅 BDAdotNetData4.vb 示例代码中的例 5(有关下载信息,请参阅本文的顶部)。

返回页首返回页首


更新

只要相关表中的行不破坏数据完整性,就可以对它们进行更新。如果这些表必须维护引用完整性,则子行必须始终引用父表中的有效行。在父表中进行的更新可以级联到子行中以维护完整性。

自动生成的 Update 命令

可以在 “数据集” 对象中更新表中的行,并针对 DataAdapter 调用 “更新” 方法,以便将所做的更改提交到数据源。DataAdapter 将基于所提供的 “删除” 命令自动生成 “更新” 命令。

在下面的代码示例中,我们将详细信息从一个订单移到另一个订单中,以便显示如何使更新操作级联:

Dim sqlConn As SqlConnection
Dim sqlDAOrder As SqlDataAdapter
Dim sqlDADetail As SqlDataAdapter
Dim sqlCmdBldrDetail As SqlCommandBuilder
Dim hierDS As DataSet
Try
   ' Create a new connection
   sqlConn = New SqlConnection(Common.getConnectionString)
   ' Create a new SqlDataAdapter object for the Order table
   sqlDAOrder = New SqlDataAdapter()
   ???‘ Create a new SqlDataAdapter object for the OrderDetails table
   sqlDADetail = New SqlDataAdapter()
   ' Create a new DataSet
   hierDS = New DataSet()
   ' Create a new SQLCommandBuilder object to autogenerate update commands
   sqlCmdBldrDetail = New SqlCommandBuilder(sqlDADetail)
   With sqlDAOrder
      ' Add a SelectCommnad object
      .SelectCommand = New SqlCommand()
      ' Specify the Select Command
      With .SelectCommand
      .CommandType = CommandType.Text
      .CommandText = "Exec GetOrderHeader @OrderId=2"
      .Connection = sqlConn
      End With
      ' Populate the DataSet with the returned data
      .Fill(hierDS, "Orders")
   End With
   With sqlDADetail
      ' Add a SelectCommnad object
      .SelectCommand = New SqlCommand()
      ' Specify the Select Command
      With .SelectCommand
         .CommandType = CommandType.Text
         .CommandText = "Exec GetOrderDetails @OrderId=2"
         .Connection = sqlConn
      End With
      ' Populate the DataSet with the returned data
      .Fill(hierDS, "Details")
   End With
   ' Establish the Foreign Key relationship between the tables
   hierDS.Relations.Add("Order_Detail", _
hierDS.Tables("Orders").Columns("OrderId"), _
            hierDS.Tables("Details").Columns("OrderId"))
   hierDS.Tables("Orders").Columns("OrderId").ReadOnly = False
   ' Move details from one order to another
   orderRow = hierDataSet.Tables("Orders").Rows(0)
   orderRow("OrderId") = 1
   ' Reconcile Changes
   sqlDADetail.Update(hierDS, "Details")
Catch E As Exception
    ' Catch Exception here
Finally
    ' Cleanup code here
End Try

注意 请参阅 DAdotNetData4.vb 示例代码中的例 6(有关下载信息,请参阅本文的顶部)。

使用 UpdateCommand 属性

自动生成的 Update 对于已移动的每一行都执行一个到数据库的往返行程。存储过程可以在单个往返行程中移动所有的详细信息行。要指定自己的 “更新” 语句,并且应当在针对 DataAdapter 调用 “更新” 方法时执行此语句,请设置 UpdateCommand 属性。可为参数化查询或存储过程设置 UpdateCommand 属性。可以按照定义 “命令” 对象的参数时使用的方法来定义 UpdateCommand 的参数:

Dim sqlConn As SqlConnection
Dim sqlDAOrder As SqlDataAdapter
Dim sqlDADetail As SqlDataAdapter
Dim hierDS As DataSet
Try
   ' Create a new connection
   sqlConn = New SqlConnection(Common.getConnectionString)
   ' Create a new SqlDataAdapter object for the Order table
   sqlDAOrder = New SqlDataAdapter()
   ' Create a new SqlDataAdapter object for the OrderDetails table
   sqlDADetail = New SqlDataAdapter()
   ' Create a new DataSet
   hierDS = New DataSet()
   With sqlDAOrder
      ' Add a SelectCommand object
      .SelectCommand = New SqlCommand()
      ' Specify the Select Command
      With .SelectCommand
         .CommandType = CommandType.Text
         .CommandText = "Exec GetOrderHeader @OrderId=1"
         .Connection = sqlConn
      End With
      ' Add a UpdateCommand object
      .UpdateCommand = New SqlCommand()
      ' Specify the Update Command
      With .UpdateCommand
         .CommandType = CommandType.StoredProcedure
         .CommandText = "MoveOrderDetails"
         ' Define parameters
         .Parameters.Add(New SqlParameter("@FromOrderId", SqlDbType.Int))
         .Parameters("@FromOrderId").Direction = ParameterDirection.Input
         .Parameters("@FromOrderId").SourceColumn = "OrderId"
         .Parameters("@FromOrderId").SourceVersion = _
 DataRowVersion.Original
         .Parameters.Add(New SqlParameter("@ToOrderId", SqlDbType.Int))
         .Parameters("@ToOrderId").Direction = ParameterDirection.Input
         .Parameters("@ToOrderId").SourceColumn = "OrderId"
         .Parameters("@ToOrderId").SourceVersion = DataRowVersion.Current
          .Connection = sqlConn
      End With
      ' Populate the DataSet with the returned data
      .Fill(hierDS, "Orders")
   End With
   With sqlDADetail
      ' Add a SelectCommand object
      sqlDADetail.SelectCommand = New SqlCommand()
      ' Specify the Select Command
      With .SelectCommand
         .CommandType = CommandType.Text
         .CommandText = "Exec GetOrderDetails @OrderId=1"
         .Connection = sqlConn
      End With
      ' Populate the DataSet with the returned data
      .Fill(hierDS, "Details")
    End With
   ' Establish the Foreign Key relationship between the tables
   hierDataSet.Relations.Add("Order_Detail", _
hierDS.Tables("Orders").Columns("OrderId"), _
            hierDS.Tables("Details").Columns("OrderId"))
   hierDS.Tables("Orders").Columns("OrderId").ReadOnly = False
   ' Move details from one order to another
   orderRow = hierDS.Tables("Orders").Rows(0)
   orderRow("OrderId") = 2
   ' Reconcile Changes
   sqlDAOrder.Update(hierDS, "Orders")
Catch E As Exception
    ' Catch Exception here
Finally
    ' Cleanup code here
End Try

注意 请参阅 BDAdotNetData4.vb 示例代码中的例 7(有关下载信息,请参阅本文的顶部)。

SourceVersion 属性允许将 OrderId 的初始值和当前值传入相应的存储过程参数以进行移动。

返回页首返回页首


删除

应当按照维护数据库完整性的方式针对分层数据执行删除。引用表中的每个子行都必须引用被引用表中的有效行。因此,只要父(被引用)表中的行与子(引用)表中的一行或多行相对应,就不应当删除这些父行,或除非将子行与相应的父行同时删除。

ADO .NET “数据集” 对象支持级联删除,这可确保当您删除被引用表中的行时,引用表中相应子行会同时被删除。可以针对 “数据集” 对象中的两个表定义外键约束,以便确定在修改这两个表中的任何行时,应当执行的操作过程。将 ForeignKeyConstraint 属性的 DeleteProperty 方法设置为某个适当的选项;它在默认情况下设置为 cascade(级联)。

自动生成的 Delete 命令

Dim sqlConn As SqlConnection
Dim sqlDAOrder As SqlDataAdapter
Dim sqlDADetail As SqlDataAdapter
Dim hierDS As DataSet
Dim sqlCmdBldrOrder As SqlCommandBuilder
Dim sqlCmdBldrDetail As SqlCommandBuilder
Try
   ' Create a new SQLConnection
   sqlConn = New SqlConnection(Common.getConnectionString)
   ' Create a new SqlDataAdapter object for the Order table
   sqlDAOrder = New SqlDataAdapter()
   ' Create a new SqlDataAdapter object for the OrderDetails table
   sqlDADetail = New SqlDataAdapter()
   ' Create a new DataSet object
   hierDS = New DataSet()
   ' Create the CommandBuilder objects for auto-generating commands
   sqlCmdBldrOrder = New SqlCommandBuilder(sqlDAOrder)
   sqlCmdBldrDetail = New SqlCommandBuilder(sqlDADetail)
   With sqlDAOrder
      ' Add a SelectCommand object
      sqlDAOrder.SelectCommand = New SqlCommand()
      ' Specify the Select Command
      With .SelectCommand
      .CommandType = CommandType.StoredProcedure
      .CommandText = "GetOrderHeaders"
      .Connection = sqlConn
      End With
      ' Populate the DataSet with the returned data
      .Fill(hierDS, "Orders")
   End With
   With sqlDADetail
      ' Add a SelectCommand object
      sqlDADetail.SelectCommand = New SqlCommand()
      ' Specify the Select Command for sqlDADetail
      With .SelectCommand
         .CommandText = "Select * from OrderDetails"
         .Connection = sqlConn
      End With
      ' Populate the DataSet with the returned data
      .Fill(hierDS, "Details")
   End With
   ' Establish a relationship between the tables
   hierDS.Relations.Add("Order_Detail", _
hierDS.Tables("Orders").Columns("OrderId"), _
            hierDS.Tables("Details").Columns("OrderId"))
   ' Find the row last row in the Orders table
   orderRow = hierDS.Tables("Orders").Rows. _
Item(hierDS.Tables("Orders").Rows.Count - 1)
   ' Delete the Row from the Orders Table. This operation will
   ' automatically delete all the corresponding child rows from the
   ' Details table in the DataSet
   orderRow.Delete()
   ???…
   ' Reconcile changes with the data source
   sqlDADetail.Update(hierDS, "Details")
   sqlDAOrder.Update(hierDS, "Orders")
Catch E As Exception
    ' Catch Exception here
Finally
    ' Cleanup code here
End Try

注意 请参阅 BDAdotNetData4.vb 示例代码中的例 8(有关下载信息,请参阅本文的顶部)。

为了维护数据完整性,必须首先从数据库中的引用表中删除子行。如果尝试先删除父表中的行,再删除其他表中相应的子行,删除操作就会失败。因此,在针对 sqlDAOrder DataAdapter 对象执行更新之前,应当针对 Details 表的 sqlDADetail DataAdapter 对象调用 Update 方法。

使用 DeleteCommand 属性

要指定自己的 “删除” 语句,并且应当在针对 DataAdapter 调用 “更新” 方法时执行此语句,请设置 DeleteCommand 属性。可对参数化查询或存储过程设置 DeleteCommand 属性。可以按照定义 “命令” 对象的参数时使用的方法,定义 DeleteCommand 的参数。

您需要设置所有参数的 SourceColumn 属性。SourceColumn 告诉 DataAdapter 对象,表中的哪一列将提供它的值。将它设置为实际的参数名:

Dim sqlConn As SqlConnection
Dim sqlDAOrder As SqlDataAdapter
Dim sqlDaDetail As SqlDataAdapter
Dim hierDS As DataSet
Try
   ' Create a new SQLConnection
   sqlConn = New SqlConnection(Common.getConnectionString)
   ' Create a new SqlDataAdapter object for the Order table
   sqlDAOrder = New SqlDataAdapter()
   ' Create a new SqlDataAdapter object for the OrderDetails table
   sqlDaDetail = New SqlDataAdapter()
   ' Create a new DataSet
   hierDS = New DataSet()
   With sqlDAOrder
      ' Add a SelectCommand object
      .SelectCommand = New SqlCommand()
      ' Specify the Select Command
      With .SelectCommand
         .CommandType = CommandType.StoredProcedure
         .CommandText = "GetOrderHeaders"
         .Connection = sqlConn
      End With
      ' Add a DeleteCommand object
      .DeleteCommand = New SqlCommand()
      ' Set the properties of the DeleteCommand
      With .DeleteCommand
         .CommandType = CommandType.StoredProcedure
         .CommandText = "DeleteOrder"
         .Connection = sqlConn
         ' Define the parameters of the stored procedure
         .Parameters.Add(New SqlParameter("@OrderId", SqlDbType.Int))
         ' Set the SourceColumn property
         .Parameters("@OrderId").SourceColumn = "OrderId"
      End With
      ' Populate the DataSet with the returned data
      .Fill(hierDS, "Orders")
   End With
   With sqlDaDetail
      ' Add a SelectCommand object
      .SelectCommand = New SqlCommand()
      ' Specify the Select Command
      With .SelectCommand
         .CommandType = CommandType.Text
         .CommandText = "Select * from OrderDetails"
         .Connection = sqlConn
      End With
      ' Populate the DataSet with the returned data
      .Fill(hierDS, "Details")
   End With
   ' Establish a relationship between the tables
   ???…
   ' Find the row last row in the Orders table
   orderRow = hierDS.Tables("Orders").Rows. _
Item(hierDS.Tables("Orders").Rows.Count - 1)
   ' Delete the Row from the Orders Table. This operation will
   ' automatically ???‘delete all the corresponding child rows from the
   ' Details table in the ???‘DataSet
   orderRow.Delete()
   ???…
   ' Reconcile changes with the data source
   sqlDAOrder.Update(hierDS, "Orders")
Catch E As Exception
    ' Catch Exception here
Finally
    ' Cleanup code here
End Try

注意 请参阅 BDAdotNetData4.vb 示例代码中的例 9(有关下载信息,请参阅本文的顶部)。

返回页首返回页首


小结

对于只读访问,DataReaderXmlReader 对象简单而又快速,尽管它们在应用程序读取数据时仍与数据源保持连接。如果应用程序保持 DataReader 或 XmlReader 对象的时间太长,以至于出现了争用现象,则这可能会限制可伸缩性。

“数据集” 对象提供一个断开连接的关系缓存,并允许以简单的方式进行分层导航和修改。级联写入简化了将所做的更改提交到数据库的过程,但是尤其在考虑往返行程内存保留和查询缓存时,自动生成的 Insert、Update 和 Delete 将不如显式编码的实现效率高。如果要在即将推出的 beta 版的 ADO .NET 中更充分地利用 SQL Server 2000 的 XML 功能,就应当解决往返行程的内存保留问题。

转到原英文地址


评论

该日志第一篇评论

发表评论

评论也有版权!