PRB:CommandBuilder 将修改后的命令复原

本文的发布号曾为 CHS310366
本文已归档。它按“原样”提供,并且不再更新。
本文引用下面的 Microsoft .NET 框架类库名称空间:
  • System.Data.SqlClient
症状
在下一次调用 DataAdapter.Update 方法时,CommandBuilder 对象可能重新生成您尝试修改的命令,因此您对该命令所作的更改可能会丢失。以下情况下可能会发生此问题:
  • 如果您将一个 CommandBuilder 对象和一个 DataAdapter 对象关联了起来。
  • 如果您使用 CommandBuilder 对象的 GetInsertCommandGetUpdateCommandGetDeleteCommand 方法显式地将命令分配给 DataAdapter
  • 如果您修改 CommandBuilder 生成的一个命令。
当您尝试调用 DataAdapter Update 方法时,可能会收到以下错误信息:
An unhandled exception of type 'System.Data.SqlClient.SqlException' occurred in system.data.dll
原因
发生此问题的原因是,CommandBuilder 动态地将它所生成的命令重新改成了原来的命令。
解决方案
可使用以下方法之一解决此问题:
  • 不要修改 CommandBuilder 生成的命令。CommandBuilder 不会更改您自己生成的命令对象。
  • InsertCommandDeleteCommandUpdateCommand 对象复制到一个新的 DataAdapter 中(有关示例,请参见"更多信息"部分)。新的 DataAdapter 变量的范围必须小于或等于旧的 DataAdapter 变量的范围。
  • 不要使用 CommandBuilder。编写您自己的命令对象,或者使用 Visual 数据工具编写命令对象。
状态
这种现象是设计使然。
更多信息

再现现象的步骤

  1. 新建一个 Visual Basic Windows 应用程序项目。默认情况下会将 Form1 添加到项目中。
  2. 双击该窗体,将下面的代码添加到"代码"窗口的顶部:
    Imports System.Data.SqlClient
  3. 在 Form1 上添加一个按钮控件。
  4. 双击该按钮,然后将下面的代码添加到 Click 事件中:
    Dim cn As New SqlConnection()Dim custDS As New DataSet()Dim da1 As New SqlDataAdapter()Dim da2 As New SqlDataAdapter()Dim dr As DataRowDim cb As SqlCommandBuildercn.ConnectionString = "server=servername;database=northwind;uid=sa;pwd=password;"cn.Open()da1 = New SqlDataAdapter("select * from Customers", cn)cb = New SqlCommandBuilder(da1)da1.Fill(custDS, "Customers")'Get the original commands.da1.InsertCommand = cb.GetInsertCommandda1.DeleteCommand = cb.GetDeleteCommandda1.UpdateCommand = cb.GetUpdateCommandDebug.WriteLine("Original command length:" & da1.InsertCommand.CommandText.Length)'Modify the Insert command.da1.InsertCommand.CommandText = "select * from customers where customerid=@@identity"da1.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecordda2.InsertCommand = da1.InsertCommandda2.DeleteCommand = da1.DeleteCommandda2.UpdateCommand = da1.UpdateCommand'Add a record to the table.Dim tblCust As DataTabletblCust = custDS.Tables("Customers")Dim drCust As DataRowdrCust = tblCust.NewRow()drCust("CustomerID") = "ZYYYY"drCust("CompanyName") = "Zora's Yummies"drCust("ContactName") = "Christophe Namby"drCust("ContactTitle") = "Assistant Manager"tblCust.Rows.Add(drCust)'Update the backend.Debug.WriteLine("Length da1 before insert:" & da1.InsertCommand.CommandText.Length)da1.Update(custDS, "Customers")Debug.WriteLine("Length da1 after insert:" & da1.InsertCommand.CommandText.Length)Debug.WriteLine("length da2 before insert:" & da2.InsertCommand.CommandText.Length)da2.Update(custDS, "Customers")Debug.WriteLine("Length da2 after insert:" & da2.InsertCommand.CommandText.Length)
  5. 将连接字符串修改为连接到您的 Microsoft SQL Server 计算机。
  6. 按 F5 键运行这段代码。
  7. 注释掉下面一行以解决上述问题:
    da1.Update(custDS, "Customers")
这个方法很有效,因为 CommandBuilderRowUpdating 事件挂钩到了 DataAdapter("da1")上,这样,它就知道何时生成命令。当您将命令复制到另一个 DataAdapter("da2")中时,CommandBuilder 就不会再挂钩到其事件中,并且不会更改事件。

变量范围很重要,因为如果"da1"和 SqlCommandBuilder("cb")超出了范围,在进行垃圾回收时,CommandBuilder 会将其命令设置为 Nothing(在 Visual C# 中为 null)。这样,就无法在"da2"中使用这些命令了。由于垃圾回收随时都有可能发生,da2.Update 方法调用可能会引发间歇性的 NullReferenceException 错误。
not saved take effect
属性

文章 ID:310366 - 上次审阅时间:02/24/2014 12:52:37 - 修订版本: 1.0

  • Microsoft ADO.NET(随 .NET 框架一起提供)
  • Microsoft Visual .NET 2002 标准版
  • kbnosurvey kbarchive kbsqlclient kbsystemdata kbprb KB310366
反馈