利用TableAdapter更新数据库

将数据保存在数据集中

保存数据就是将应用程序中的已更改数据保存回原始数据存储区(通常是关系数据库,如 SQL Server)的过程。

因为数据集是有效的数据缓存(数据在内存中的副本),所以将信息写入初始数据源的进程与在数据集中修改数据的进程是分开的。 通过调用 TableAdapter 的一个 Update 方法,或调用 TableAdapter 的一个 DBDirect 方法,可以将数据集中的已更新数据发送回数据库。

有关将数据集中的更改发送回数据集的更多信息,请参见如何:使用 TableAdapter 更新数据如何:将数据集更改保存到数据库中

Visual Studio 提供了一个 TableAdapterManager 组件,该组件可帮助您保存相关表中的数据。 此组件可确保按照数据库中定义的外键约束来以正确的顺序执行保存。 有关更多信息,请参见分层更新概述

有关修改数据集中的数据的信息,请参见 在应用程序中编辑数据

两步更新

通过数据集更新数据源是一个包含两个步骤的过程。 第一步是使用新信息(新记录、已更改的记录或已删除的记录)更新数据集。 如果您的应用程序只与数据集有关(例如在更新数据集后,将其发送给另一个应用程序,由它对数据集执行进一步处理),那么您就完成了更新。

 提示

在 Windows 窗体中,数据绑定结构负责将更改从数据绑定控件发送到数据集,您不必用自己的代码显式更新数据集。 有关更多信息,请参见 Windows 窗体数据绑定

如果您正在更新数据源(如数据库),第二步则是将更改从数据集发送到初始数据源。 也就是说,更新数据集的过程不会同时将更改直接写入基础数据源,所以您必须显式执行第二步。 为了完成这一步骤,您通常会调用曾用来填充数据集的同一个 TableAdapter(或数据适配器)的 Update 方法(虽然您也可以使用不同的适配器,例如为了将数据从一个数据源移动到另一个数据源或更新多个数据源)。

两步更新过程和 DataRowVersion 在成功更新中的作用

Visual Basic 数据集更新

在结构上,数据集使数据可以作为多组集合来使用。 数据集包含表的集合。 表包含行的集合。 表公开为 DataSet 对象的集合,记录在 DataTable 对象的 Rows 集合中可用。 您可以通过使用基集合方法操作这些集合来对数据集中的数据进行更改,但是如果您要更新基础数据源,则必须使用专用于数据集修改的方法。

例如,要从数据表中移除一个记录,可以调用该表的 Rows 集合的 RemoveAt 方法,它会从数据集中实际删除该记录。 如果您只是将数据集用作数据的结构化存储区,而不考虑将更改信息传送到另一个应用程序,那么以这种方式操作集合就是一种可以接受的数据集更新方式。

但是,如果您要将更改发送到数据源或另一个应用程序,则需要维护有关每次更新的更改信息(即元数据)。 随后,在将更改发送到数据源或应用程序时,该过程将使用它所需的信息来查找和更新适当的记录。 例如,如果删除数据集中的一个记录,则必须在数据集中维护有关该删除记录的信息。 这样,当调用 TableAdapter 的 DeleteCommand 时,就会有足够的历史信息来查找数据源中的初始记录,从而可将其删除。 有关更多信息,请参见下面的“维护有关更改的信息”。

合并数据集

您可以通过合并来更新数据集的内容,合并就是将称作“源”的数据集的内容复制到调用数据集(称作“目标”数据集)中。 当合并数据集时,源数据集中的新记录将添加到目标数据集中。 另外,源数据集中额外的列也会添加到目标数据集中。 当您具有一个本地数据集然后又从另一个应用程序或组件(如 XML Web services)获得另外一个数据集时,合并数据集就会特别有用。 在需要集成来自多个数据集中的数据时,它也十分有用。

合并数据集时,还可以传递一个可选的布尔型参数 (preserveChanges),它指示 Merge 方法是否在目标数据集中保留现有的修改。 由于数据集维护记录的多个版本,所以务必要记住所合并的是记录的多个版本。 下表阐释了将要合并的两个数据集中的一个记录:

合并数据集

DataRowVersion

目标数据集

源数据集

原始

James Wilson

James C. Wilson

当前

Jim Wilson

James C. Wilson

在 preserveChanges=false targetDataset.Merge(sourceDataset) 的情况下,对上表调用 Merge 方法将产生以下结果:

合并数据集

DataRowVersion

目标数据集

源数据集

原始

James C. Wilson

James C. Wilson

当前

James C. Wilson

James C. Wilson

在 preserveChanges = true targetDataset.Merge(sourceDataset, true) 的情况下调用 Merge 方法将产生以下结果:

表 3

DataRowVersion

目标数据集

源数据集

原始

James C. Wilson

James C. Wilson

当前

Jim Wilson

James C. Wilson

 警告

在 preserveChanges = true 的情况下,如果对目标数据集中的记录调用 RejectChanges 方法,它将还原为源数据集中的初始数据。 这意味着如果您要用目标数据集更新初始数据源,则可能无法找到要更新的初始行。 但是,通过用数据源中已更新的记录填充另一个数据集,然后执行防止并发冲突的合并,可以防止出现并发冲突。 (当另一个用户在数据集被填充后修改数据源中的记录时,会发生并发冲突。)有关更多信息,请参见 ADO.NET 中的并发控制

更新约束

要对现有的数据行进行更改,可以在个别列中添加或更新数据。 如果数据集包含约束(例如外键或不可为 null 的约束),那么当您更新记录时(在更新完一列之后,但在开始更新下一列之前),记录可能会暂时处于错误状态。

为了避免这种早产的约束冲突,可以暂时挂起更新约束。 这可达到两个目的:

  • 防止在更新完一列后,开始更新另一列之前引发错误。

  • 暂停引发某些更新事件(常用于冲突的事件)。

完成更新后,可以重新启用约束检查,约束检查还重新启用更新事件并引发它们。

 提示

在 Windows 窗体中,内置在数据网格中的数据绑定结构会暂停约束检查,直到焦点移出行外为止,因此您不必显式调用 BeginEditEndEdit 或 CancelEdit 方法。

当对数据集调用 Merge 方法时,将自动禁用约束。 当合并完成后,如果数据集上有任何无法启用的约束,则引发 ConstraintException。 在这种情况下,EnforceConstraints 属性将设置为 false,并且在将 EnforceConstraints 属性重新设置为 true 之前,必须解决所有约束冲突。

完成更新后,可以重新启用约束检查,约束检查还重新启用更新事件并引发它们。

有关暂停事件的更多信息,请参见如何:在填充数据集时关闭约束

数据集更新错误

当更新数据集中的记录时,可能出现错误。 例如,您可能在无意中将数据类型错误、太长或具有某些其他完整性问题的数据写入一个列中。 此外,您可能拥有应用程序特定的验证检查,它们可在更新事件的任何阶段引发自定义的错误。 有关更多信息,请参见 验证数据集中的数据

维护有关更改的信息

有关数据集中更改的信息可以通过以下两种方式来进行维护:通过对行进行标记,指示该行是否已更改 (RowState);以及通过保留记录的多个副本 (DataRowVersion)。 利用这些更改信息,进程可以确定数据集中有哪些更改,并且可以将适当的更新发送到数据源。

RowState 属性

DataRow 对象的 RowState 属性是一个值,它提供有关特定数据行状态的信息。

下表详细说明 DataRowState 枚举的可能值:

ROWSTATE 属性

DataRowState 值

说明

Added

该行已作为一项添加到 DataRowCollection。 (处于这种状态的行不具有相应的初始版本,因为在最近一次调用 AcceptChanges 方法时它尚不存在)。

Deleted

已使用 DataRow 对象的 Delete 删除了该行。

Detached

已创建该行,但它不是任何 DataRowCollection 的一部分。 在 DataRow 对象刚创建之后但在添加到集合之前,或者如果该对象已从集合中移除,它将处于这种状态。

Modified

该行中的列值已通过某种方式更改。

Unchanged

自上一次调用 AcceptChanges 之后,该行未更改。

DataRowVersion 枚举

数据集维护记录的多个版本。 DataRow 对象的 DataRowVersion 枚举是一个值,它可用于返回 DataRow 对象的特定版本。

下表详细说明 DataRowVersion 枚举的可能值:

DATAROWVERSION 枚举

DataRowVersion 值

说明

Current

记录的当前版本包含自上次调用 AcceptChanges 后对记录执行的所有修改。 如果该行已被删除,则没有当前版本。

Default

记录的默认值,如数据集架构或数据源所定义的一样。

Original

记录的初始版本是在数据集中最后一次提交更改时记录的副本。 实际上,它通常是从数据源中读取的记录版本。

Proposed

更新过程当中(即在调用 BeginEdit 方法和调用 EndEdit 方法之间)临时可用的记录的建议版本。 通常在事件(例如 RowChanging)处理程序中访问记录的建议版本。 调用 CancelEdit 方法将撤消更改并删除数据行的建议版本。

当更新信息传送到数据源时,初始版本和当前版本十分有用。 通常,当更新被发送到数据源时,数据库的新信息处于记录的当前版本中。 来自初始版本的信息用于查找要更新的记录。 例如,在更改了记录主键的情况下,为了将该更改更新到数据源,必须通过某种方式在数据源中查找适当的记录。 如果不存在初始版本,则很可能会将该记录追加到数据源,这不仅会导致一项额外的多余记录,而且还会导致一条不准确并且已过期的记录。 这两个版本也用于并发控制;您可以将初始版本与数据源中的记录进行比较,以确定该记录自加载到数据集之后是否经过更改。 有关更多信息,请参见 ADO.NET 中的并发控制

当您需要在将更改实际提交到数据集之前执行验证时,可以使用建议版本。

即使记录已更改,也不总会有该行的初始版本或当前版本。 当您将新行插入表中时,没有初始版本,只有当前版本。 同样,如果通过调用表的 Delete 方法删除一行,则只有初始版本,没有当前版本。

您可以通过查询数据行的 HasVersion 方法,来测试记录的某个特定版本是否存在。 当请求列的值时,通过将 DataRowVersion 枚举值作为可选参数进行传递,可以访问记录的任何一个版本。

获取已更改的记录

通常,不会对数据集中的每个记录进行更新。 例如,用户可能正在处理一个显示很多记录的 Windows 窗体 DataGridView 控件。 但是,用户可能只更新了几个记录,删除了一个记录,并插入了一个新记录。 数据集和数据表提供了一种只返回已修改行的方法 (GetChanges)。

可以使用数据表的 GetChanges 方法 (GetChanges) 或数据集本身的该方法 (GetChanges) 来创建已更改记录的子集。 如果对数据表调用该方法,它将返回只包含已更改记录的数据表副本。 同样,如果对数据集调用该方法,将获得一个新的数据集,其中只包含已更改的记录。 GetChanges 本身将返回所有已更改的记录。 然而,通过将所需的 DataRowState 作为参数传递给 GetChanges 方法,则可以指定所需的更改记录子集:新添加的记录、标记为删除的记录、分离的记录或已修改的记录。

当需要将记录发送给另一个组件进行处理时,获取已更改记录的子集非常有用。 通过只获取该组件所需的记录而不是发送整个数据集,可以降低与其他组件通信的系统开销。 有关更多信息,请参见如何:检索已更改的行

提交数据集中的更改

当在数据集中做出更改时,即设置了所更改行的 RowState 属性。 此时将建立并维护记录的初始版本和当前版本,并通过 RowVersion 属性供您使用。 要将适当的更新发送给数据源,需要使用存储在这些属性中表示更改的元数据。

如果更改反映了数据源的当前状态,则不再需要维护该信息。 通常,数据集及其数据源在以下两个时间是同步的:

  • 刚刚将信息加载到数据集之后,例如从数据源读取数据时。

  • 将更改从数据集发送到数据源之后(但不是在这之前,因为您可能丢失将更改发送到数据库所需的更改信息)。

通过调用 AcceptChanges 方法,可以将挂起的更改提交到数据集。 通常,在以下时间将在应用程序中调用 AcceptChanges

  • 在加载数据集之后。 如果通过调用 TableAdapter 的 Fill 方法来加载数据集,该适配器将自动为您提交更改。 但是,如果通过将另一个数据集合并到其中来加载数据集,则必须手动提交更改。

     提示

    通过将适配器的 AcceptChangesDuringFill 属性设置为 false,可以防止在调用 Fill 方法时适配器自动提交更改。 如果它设置为 false,则在填充过程中插入的每行的 RowState 都将设置为 Added

  • 在将数据集更改发送到另一个进程(例如 XML Web service)之后。

     警告

    以这种方法提交更改会清除所有更改信息。 在执行应用程序确信已知在数据集中作出何种更改的任何操作之前,请不要提交更改。

该方法完成以下操作:

AcceptChanges 方法可以在三个级别上使用。 可以对 DataRow 对象调用该方法,它只提交该行的更改。 还可以对 DataTable 对象调用该方法,以提交表中所有行的更改,或者对 DataSet 对象调用该方法,以提交数据集中所有表的所有记录的所有挂起的更改。

下表基于对何种对象调用该方法说明所提交的更改。

表 6

方法

结果

DataRow.AcceptChanges

只提交特定行的更改

DataTable.AcceptChanges

提交特定表中所有行的更改

DataSet.AcceptChanges

提交数据集中所有表的所有行的更改

 提示

如果通过调用 TableAdapter 的 Fill 方法加载数据集,则无需显式接受更改;默认情况下,Fill 方法会在填充完数据表后调用 AcceptChanges 方法。

相关方法 RejectChanges 撤消更改的影响,方法是将记录的 Original 版本复制回 Current 版本中,并将每个记录的 RowState 设置回 Unchanged

数据验证

为了验证应用程序中的数据符合它将传递到的进程的要求,通常需要添加验证。 这可能会涉及到检查用户在窗体中的输入是否正确,验证另一个应用程序向您的应用程序发送的数据,甚至检查在您的组件中计算的信息是否符合数据源约束和应用程序要求。

您可以通过多种方法验证数据:

  • 在业务层,将代码添加到应用程序中以便验证数据。 数据集是可以执行此操作的一个位置。 数据集提供了后端验证的某些优点,例如能够在更改列和行值时验证更改。 有关更多信息,请参见 验证数据集中的数据

  • 在表示层,向窗体添加验证。 有关更多信息,请参见Windows 窗体中的用户输入验证

  • 在数据后端,将数据发送到数据源(例如数据库)并允许它接受或拒绝该数据。 如果您正在使用一个数据库,而该数据库具有复杂的功能来验证数据并提供错误信息,则这将是一种实用的方法,因为无论数据来自何处,您都可以对数据进行验证。 但是,它可能不适合应用程序特定的验证的需要。 另外,根据您的应用程序如何解决由后端引发的验证错误,让数据源验证数据可能导致大量到数据源的往返过程。

    表 7
    安全说明安全说明

    在使用将 CommandType 属性设置为 Text 的数据命令时,将从客户端发送的信息传递到数据库前请仔细检查该信息。 恶意用户可能会尝试发送(插入)修改过的或其他 SQL 语句,以获得未经授权的访问或破坏数据库。 在将用户输入内容传输到数据库之前,应始终确认这些信息是有效的;如果可能的话,请始终使用参数化查询或存储过程,这是最佳做法。 有关更多信息,请参见脚本侵入概述

当在数据集中作出更改之后,可以将更改传递给数据源。 完成此操作时最常用的方法是调用 TableAdapter(或数据适配器)的 Update 方法。 该方法依次通过数据表中的每个记录,确定需要什么类型的更新(更新、插入或删除),然后执行适当的命令(如果有)。

如何将更新传递给数据源

为了阐释如何进行更新,假设您的应用程序使用一个包含单个数据表的数据集。 该应用程序从数据库中获取两行。 在检索之后,内存中的数据表类似于下面这样:

(RowState)     CustomerID   Name             Status
(Unchanged)    c200         Robert Lyon      Good
(Unchanged)    c400         Nancy Buchanan    Pending

应用程序将 Nancy Buchanan 的状态更改为“Preferred”。作为这种更改的结果,该行的 RowState 属性的值从 Unchanged 更改为 Modified。 第一行的 RowState 属性的值保持为 Unchanged。 数据表现在类似于下面这样:

(RowState)     CustomerID   Name             Status
(Unchanged)    c200         Robert Lyon      Good
(Modified)     c400         Nancy Buchanan    Preferred

应用程序现在调用 Update 方法,将数据集传送给数据库。 该方法依次检查每一行。 对于第一行,由于它从最初取自数据库后未经过更改,所以该方法不向数据库传送任何 SQL 语句。

但是对于第二行,Update 方法将自动调用适当的数据命令并将其传送给数据库。 SQL 语句的具体语法取决于基础数据存储区所支持的 SQL 语言分支。 但所传送的 SQL 语句具有下列值得注意的一般特征:

  • 所传送的 SQL 语句是一个 UPDATE 语句。 因为 RowState 属性的值为 Modified,所以适配器知道应使用 UPDATE 语句。

  • 所传送的 SQL 语句包含一个 WHERE 子句,它指示 UPDATE 语句的目标是 CustomerID = 'c400' 的行。 由于 CustomerID 是目标表的主键,SELECT 语句的这一部分会将目标行与其他所有行区分开来。 WHERE 子句的信息是从记录 (DataRowVersion.Original) 的初始版本导出的,以备在识别行所需的值已被更改的情况下使用。

  • 所传送的 SQL 语句包含 SET 子句,用以设置已修改列的新值。

     提示

    如果 TableAdapter 的 UpdateCommand 属性已设置为存储过程的名称,则适配器不会构造 SQL 语句。 而是使用传入的适当参数调用存储过程。

传递参数

数据库中要更新的记录的值通常使用参数来进行传递。 当 TableAdapter 的 Update 方法执行 UPDATE 语句时,它需要填写参数值。 它从 Parameters 集合中为适当的数据命令(在本例中是 TableAdapter 中的 UpdateCommand 对象)获取这些值。

如果您已使用 Visual Studio 工具生成数据适配器,则 UpdateCommand 对象将包含一个参数集合,这些参数对应于语句中的每个参数占位符。

每个参数的 SqlParameter.SourceColumn 属性都指向数据表中的一个列。 例如,au_id 和 Original_au_id 参数的 SourceColumn 属性设置为包含作者 ID 的数据表中的任意一列。 当适配器的 Update 方法运行时,它从所更新的记录中读取作者 ID 列,并将值填充到语句中。

在 UPDATE 语句中,您需要指定新值(将写入记录的值)和旧值(以便在数据库中查找要更新的记录)。 因此,每个值都有两个参数:一个用于 SET 子句,另一个则用于 WHERE 子句。 两个参数都从所更新的记录中读取数据,但根据参数的 SqlParameter.SourceVersion 属性,它们会获取该列值的不同版本。 SET 子句的参数获取当前版本,而 WHERE 子句的参数获取初始版本。

 提示

您还可以自己在代码中设置 Parameters 集合中的值,这通常是在数据适配器的 RowChanging 事件的事件处理程序中进行的。

更新相关表

如果数据集包含多个表,则必须通过分别调用每个数据适配器的 Update 方法来逐个更新这些表。 如果这些表具有父子关系,很可能需要以特定的顺序将更新发送到数据库。 常见的情况是已经将父记录和相关子记录添加到数据集中,例如一个新的客户记录及一个或多个相关的订单记录。 如果数据库本身强制关系完整性规则,那么当您在创建父记录之前将新的子记录发送到数据库时,数据库将引发错误。

相反,如果删除数据集中的相关记录,您通常需要以相反的顺序发送更新:先发送子表,然后发送父表。 否则,数据库很可能引发错误,因为引用完整性规则将阻止您在相关子记录仍存在的情况下删除父记录。

为相关表发送更新的一般规则遵循以下顺序:

  1. 子表:删除记录。

  2. 父表:插入、更新和删除记录。

  3. 子表:插入和更新记录。

  4. 有关更多信息,请参见演练:将数据保存到数据库(多个表)

并发控制

由于数据集与数据源是分开的,您并不持有数据源中记录上的锁。 因此,如果要更新数据库,并且如果维护并发控制对应用程序来说十分重要,则必须协调数据集中的记录和数据库中的记录。 例如,您可能发现数据库中的记录在上一次填充数据集之后已经更改。 在这种情况下,必须执行适合于应用程序的逻辑来指定如何处理数据库记录或数据集中已更改的记录。 有关更多信息,请参见 ADO.NET 中的并发控制

请参见

任务

如何:使用 TableAdapter 更新数据

概念

TableAdapter 概述

在 Visual Studio 中将控件绑定到数据

其他资源

Visual Studio 的数据应用程序概述

连接到 Visual Studio 中的数据

准备应用程序以接收数据

将数据获取到应用程序

在应用程序中编辑数据

验证数据

保存数据


 

如何:将数据集更改保存到数据库中

在修改并验证了数据集中的数据后,可能需要将更新后的数据发回数据库。 为了向数据库发送修改的数据,您可调用 TableAdapter 或数据适配器的 Update 方法。 此适配器的 Update 方法将更新单个数据表并根据该表中每个数据行的 RowState 执行正确的命令(INSERT、UPDATE 或 DELETE)。

将数据保存在相关表中时,Visual Studio 提供的 TableAdapterManager 组件有助于根据在数据库中定义的外键约束以正确的顺序执行保存。 有关更多信息,请参见分层更新概述

 提示

由于尝试使用数据集的内容更新数据源可能会导致错误,因此应将调用该适配器的 Update 方法的代码放置在 try/catch 块的内部。

根据您的业务需要,更新数据源的确切过程可能会有所不同,但是您的应用程序应该包括以下步骤:

  1. 执行 try/catch 块中尝试将更新发送到数据库的代码。

  2. 如果捕获到异常,则找到引发错误的数据行。 有关更多信息,请参见如何:定位出错的行

  3. 协调数据行中的问题(在可能的情况下以编程的方式进行,或者将无效的行显示给用户进行修改),然后重新尝试更新(HasErrors 属性,GetErrors 方法)。

将数据保存到数据库

调用 TableAdapter 或数据适配器的 Update 方法,将包含要写入值的数据表的名称传递给数据库。 有关将单个数据表中的数据保存回数据库的更多信息,请参见演练:将数据保存到数据库(单个表)

通过 TableAdapter 用数据集更新数据库

  • 在 try/catch 块中包含 TableAdapter.Update 方法。 下面的示例演示如何尝试用 NorthwindDataSet 中的 Customers 表的内容进行更新。

    C#
    try
    {
        this.Validate();
        this.customersBindingSource.EndEdit();
        this.customersTableAdapter.Update(this.northwindDataSet.Customers);
        MessageBox.Show("Update successful");
    }
    catch (System.Exception ex)
    {
        MessageBox.Show("Update failed");
    }
    

使用数据适配器用数据集更新数据库

  • 在 try/catch 块中包含 DataAdapter.Update 方法。 下面的示例演示如何尝试用 DataSet1 中的 Table1 的内容对数据源进行更新。

    C#
    try
    {
        SqlDataAdapter1.Update(Dataset1.Tables["Table1"]);
    }
    catch (Exception e)
    {
        // Error during Update, add code to locate error, reconcile 
        // and try to update again.
    }
    

更新数据集中的两个相关表

当更新数据集中的相关表时,务必要以正确的顺序进行更新,以减小违反引用完整性约束的可能性。 命令执行的顺序也将遵循数据集中 DataRowCollection 的索引的顺序。 为了防止引发数据完整性错误,最佳做法是按照下面的顺序更新数据库:

  1. 子表:删除记录。

  2. 父表:插入、更新和删除记录。

  3. 子表:插入和更新记录。

有关从多个表保存数据的详细信息,请参见演练:将数据保存到数据库(多个表)

如果要更新两个或更多相关表,则应将所有更新逻辑包括在一个事务内。 事务是指一个过程,它首先确保对数据库的所有相关更改均可成功完成,然后再提交更改。 有关更多信息,请参见事务和并发 (ADO.NET)

使用 TableAdapter 更新两个相关表

  1. 创建三个临时 DataTable 以保存不同的记录。

  2. 从 try/catch 块中为每个子行集调用 Update 方法。 如果出现更新错误,建议的操作过程则将分支并解除错误。

  3. 将更改从数据集提交到数据库。

  4. 处置临时数据表以释放资源。

    C#
    void UpdateDB()
    {
        NorthwindDataSet.OrdersDataTable deletedChildRecords = 
            (NorthwindDataSet.OrdersDataTable)northwindDataSet.Orders.GetChanges(DataRowState.Deleted);
    
        NorthwindDataSet.OrdersDataTable newChildRecords = 
            (NorthwindDataSet.OrdersDataTable)northwindDataSet.Orders.GetChanges(DataRowState.Added);
    
        NorthwindDataSet.OrdersDataTable modifiedChildRecords = 
            (NorthwindDataSet.OrdersDataTable)northwindDataSet.Orders.GetChanges(DataRowState.Modified);
    
        try
        {
            if (deletedChildRecords != null)
            {
                ordersTableAdapter.Update(deletedChildRecords);
            }
    
            customersTableAdapter.Update(northwindDataSet.Customers);
    
            if (newChildRecords != null)
            {
                ordersTableAdapter.Update(newChildRecords);
            }
    
            if (modifiedChildRecords != null)
            {
                ordersTableAdapter.Update(modifiedChildRecords);
            }
    
            northwindDataSet.AcceptChanges();
        }
    
        catch (Exception ex)
        {
            MessageBox.Show("An error occurred during the update process");
            // Add code to handle error here.
        }
    
        finally
        {
            if (deletedChildRecords != null)
            {
                deletedChildRecords.Dispose();
            }
            if (newChildRecords != null)
            {
                newChildRecords.Dispose();
            }
            if (modifiedChildRecords != null)
            {
                modifiedChildRecords.Dispose();
            }
        }
    }
    

使用数据适配器更新两个相关表

  • 调用每个数据适配器的 Update 方法。

    下面的示例显示如何用包含相关表的数据集更新数据源。 为了遵循上面的顺序,将创建三个临时 DataTable 来保存不同的记录。 然后,将在 try/catch 块中对每个子行集调用 Update 方法。 如果出现更新错误,建议的操作过程则将分支并解除错误。 然后,数据集提交更改。 最后,处置临时数据表以释放资源。

    C#
    void UpdateDB()
    {
        System.Data.DataTable DeletedChildRecords = 
            dsNorthwind1.Orders.GetChanges(System.Data.DataRowState.Deleted);
    
        System.Data.DataTable NewChildRecords = 
            dsNorthwind1.Orders.GetChanges(System.Data.DataRowState.Added);
    
        System.Data.DataTable ModifiedChildRecords = 
            dsNorthwind1.Orders.GetChanges(System.Data.DataRowState.Modified);
    
        try
        {
            if (DeletedChildRecords != null)
            {
                daOrders.Update(DeletedChildRecords);
            }
            if (NewChildRecords != null)
            {
                daOrders.Update(NewChildRecords);
            }
            if (ModifiedChildRecords != null)
            {
                daOrders.Update(ModifiedChildRecords);
            }
    
            dsNorthwind1.AcceptChanges();
        }
    
        catch (Exception ex)
        {
            // Update error, resolve and try again
        }
    
        finally
        {
            if (DeletedChildRecords != null)
            {
                DeletedChildRecords.Dispose();
            }
            if (NewChildRecords != null)
            {
                NewChildRecords.Dispose();
            }
            if (ModifiedChildRecords != null)
            {
                ModifiedChildRecords.Dispose();
            }
        }
    }
    

请参见

概念

数据应用程序开发中的新增功能

在 Visual Studio 中将 Windows 窗体控件绑定到数据

在 Visual Studio 中将控件绑定到数据

其他资源

数据演练

连接到 Visual Studio 中的数据

准备应用程序以接收数据

将数据获取到应用程序

在应用程序中编辑数据

验证数据

保存数据


 

如何:使用 TableAdapter 更新数据

更新:2011 年 4 月

在修改并验证了数据集中的数据后,可能需要将更新后的数据发回数据库。 要将修改后的数据发送到数据库,需要调用 TableAdapter 的 Update 方法。 此适配器的 Update 方法将更新单个数据表并根据该表中每个数据行的 RowState 执行正确的命令(INSERT、UPDATE 或 DELETE)。 当您将数据保存在相关表中时,Visual Studio 提供的 TableAdapterManager 组件有助于根据在数据库中定义的外键约束以正确的顺序执行保存。 有关更多信息,请参见分层更新概述

 提示

由于尝试使用数据集的内容更新数据源可能会导致错误,因此应将调用该适配器的 Update 方法的代码放入 try/catch 块内。

根据不同的业务需求,更新数据源的确切过程可能会有所不同,但是您的应用程序应该包括以下步骤:

  1. 在 try/catch 块中调用适配器的 Update 方法。

  2. 如果捕获到异常,则找到引发错误的数据行。 有关更多信息,请参见如何:定位出错的行

  3. 解决数据行中的问题(在可能的情况下以编程方式进行,或者将无效的行提供给用户进行修改),然后重新尝试更新(HasErrorsGetErrors)。

将数据保存到数据库

调用 TableAdapter 的 Update 方法,传递数据表的名称,该数据表包含要写入数据库的值。

将数据保存到数据库
重要说明重要事项

使用本地数据库(如 .mdf 文件)时,不能将文件的“复制到输出”属性设置为“始终复制”。 如果该属性设置为“始终复制”,则生成项目时,文件将覆盖对本地数据库所做的任何更改。 若要更正此问题,请在解决方案资源管理器中右击该文件,单击“属性”,并更改“复制到输出”的值。

使用 TableAdapter 更新具有数据集的数据库

  • 在 try/catch 块中包含适配器的 Update 方法。 下面的示例演示如何尝试用 NorthwindDataSet 中的 Customers 表的内容从 try/catch 块内部执行更新。

    C#
    try
    {
        this.Validate();
        this.customersBindingSource.EndEdit();
        this.customersTableAdapter.Update(this.northwindDataSet.Customers);
        MessageBox.Show("Update successful");
    }
    catch (System.Exception ex)
    {
        MessageBox.Show("Update failed");
    }
    

使用 TableAdapter 更新数据集中的两个相关表

更新数据集中的相关表时,为了减小违反引用完整性约束的可能性,必须以正确的顺序进行更新。 命令执行的顺序也将遵循数据集中 DataRowCollection 的索引的顺序。 为了防止引发数据完整性错误,最佳做法是按照下面的顺序更新数据库:

  1. 子表:删除记录。

  2. 父表:插入、更新和删除记录。

  3. 子表:插入和更新记录。

     提示

    如果要更新两个或更多相关表,应将所有更新逻辑包括在一个事务内。 事务是指一个过程,它在提交任何更改之前都先要确保对数据库的所有相关更改均可成功完成。 有关更多信息,请参见事务和并发 (ADO.NET)

使用 TableAdapter 更新两个相关表

  1. 创建三个临时数据表以保存不同的记录。

  2. 从 try/catch 块中为每个子行集调用 Update 方法。 如果发生更新错误,则应分支出来并解决这些错误。

  3. 将更改提交到数据库。

  4. 处置临时数据表以释放资源。

    下面的示例显示如何用包含相关表的数据集更新数据源。

    C#
    void UpdateDB()
    {
        NorthwindDataSet.OrdersDataTable deletedChildRecords = 
            (NorthwindDataSet.OrdersDataTable)northwindDataSet.Orders.GetChanges(DataRowState.Deleted);
    
        NorthwindDataSet.OrdersDataTable newChildRecords = 
            (NorthwindDataSet.OrdersDataTable)northwindDataSet.Orders.GetChanges(DataRowState.Added);
    
        NorthwindDataSet.OrdersDataTable modifiedChildRecords = 
            (NorthwindDataSet.OrdersDataTable)northwindDataSet.Orders.GetChanges(DataRowState.Modified);
    
        try
        {
            if (deletedChildRecords != null)
            {
                ordersTableAdapter.Update(deletedChildRecords);
            }
    
            customersTableAdapter.Update(northwindDataSet.Customers);
    
            if (newChildRecords != null)
            {
                ordersTableAdapter.Update(newChildRecords);
            }
    
            if (modifiedChildRecords != null)
            {
                ordersTableAdapter.Update(modifiedChildRecords);
            }
    
            northwindDataSet.AcceptChanges();
        }
    
        catch (Exception ex)
        {
            MessageBox.Show("An error occurred during the update process");
            // Add code to handle error here.
        }
    
        finally
        {
            if (deletedChildRecords != null)
            {
                deletedChildRecords.Dispose();
            }
            if (newChildRecords != null)
            {
                newChildRecords.Dispose();
            }
            if (modifiedChildRecords != null)
            {
                modifiedChildRecords.Dispose();
            }
        }
    }
    

如何:将新记录插入数据库

若要将新记录插入数据库中,您可以使用 TableAdapter.Update 方法,或 TableAdapter 的 DBDirect 方法之一(特别是 TableAdapter.Insert 方法)。 有关更多信息,请参见 TableAdapter 概述

如果您的应用程序不使用 TableAdapters,您就可以使用命令对象与数据库交互并插入新记录到其中(例如,SqlCommand)。

当应用程序使用数据集存储数据时,请使用 TableAdapter.Update 方法。 Update 方法会将所有更改(包括更新、插入以及删除)发送到数据库中。

当应用程序使用对象存储数据时,或者您要对在数据库中创建新记录进行更好的控制时,请使用 TableAdapter.Insert 方法。

如果 TableAdapter 没有 Insert 方法,则意味着或者该 TableAdapter 是为使用存储过程而配置的,或者其 GenerateDBDirectMethods 属性已被设置为 false。 尝试从“数据集设计器”内将 TableAdapter 的 GenerateDBDirectMethods 属性设置为 true,然后保存该数据集以重新生成 TableAdapter。 如果 TableAdapter 仍没有 Insert 方法,则该表可能没有提供足够的架构信息以区分各行(例如,表中未设置主键)。

使用 TableAdapter 插入新记录

根据应用程序的需要,TableAdapter 提供了将新记录插入数据库的不同方法。

如果应用程序使用数据集存储数据,则可以完全将新记录添加到数据集中所需的 DataTable,然后调用 TableAdapter.Update 方法。 TableAdapter.Update 方法得到 DataTable 中的所有更改,并将这些更改发送到数据库中(包括已修改的记录和删除的记录)。

使用 TableAdapter.Update 方法将新记录插入数据库

  1. 通过创建新的 DataRow 并将它添加到 Rows 集合中,可将新记录添加到所需的 DataTable 中。 有关更多信息,请参见如何:向数据表中添加行

  2. 将新行添加到 DataTable 后,请调用 TableAdapter.Update 方法。 通过传入完整的 DataSetDataTableDataRow 数组或单个 DataRow,您可以控制要更新的数据量。

    下面的代码显示如何将新记录添加到 DataTable 中,然后调用 TableAdapter.Update 方法将新行保存到数据库中。 (此示例使用 Northwind 数据库的 Region 表。)

    C#
    // Create a new row.
    NorthwindDataSet.RegionRow newRegionRow;
    newRegionRow = northwindDataSet.Region.NewRegionRow();
    newRegionRow.RegionID = 5;
    newRegionRow.RegionDescription = "NorthWestern";
    
    // Add the row to the Region table
    this.northwindDataSet.Region.Rows.Add(newRegionRow);
    
    // Save the new row to the database
    this.regionTableAdapter.Update(this.northwindDataSet.Region);
    

如果应用程序使用对象存储应用程序中的数据,您就可以使用 TableAdapter.Insert 方法直接在数据库中创建新行。 Insert 方法接受将每一列的单个值作为参数。 调用此方法用传入的参数值将新记录插入到数据库。

以下过程使用 Northwind 数据库的 Region 表作为示例。

使用 TableAdapter.Insert 方法将新记录插入到数据库

  • 调用 TableAdapter 的 Insert 方法,为每一列传入值作为参数。

     提示

    如果没有实例可用,请实例化您要使用的 TableAdapter。

    C#
    NorthwindDataSetTableAdapters.RegionTableAdapter regionTableAdapter = 
        new NorthwindDataSetTableAdapters.RegionTableAdapter();
    
    regionTableAdapter.Insert(5, "NorthWestern");
    

使用命令对象插入新记录

下面的示例使用命令对象直接将新记录插入到数据库。 有关使用命令对象执行命令和存储过程的更多信息,请参见 将数据获取到应用程序

以下过程使用 Northwind 数据库的 Region 表作为示例。

使用命令对象将新记录插入到数据库

  • 创建新的命令对象,并设置它的 Connection、CommandType 和 CommandText 属性。

    C#
    System.Data.SqlClient.SqlConnection sqlConnection1 = 
        new System.Data.SqlClient.SqlConnection("YOUR CONNECTION STRING");
    
    System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand();
    cmd.CommandType = System.Data.CommandType.Text;
    cmd.CommandText = "INSERT Region (RegionID, RegionDescription) VALUES (5, 'NorthWestern')";
    cmd.Connection = sqlConnection1;
    
    sqlConnection1.Open();
    cmd.ExecuteNonQuery();
    sqlConnection1.Close();
    

安全性

您必须有访问正尝试连接到的数据库的权限,以及在所需表中执行插入的权限。


 

如何:更新数据库中的记录

更新:2010 年 9 月

可以使用 TableAdapter.Update 方法更新(编辑)数据库中的记录。 TableAdapter.Update 方法根据传入的参数提供了若干次执行不同操作的重载。 了解调用这些不同方法签名的结果非常重要。

 提示

如果您的应用程序不使用 TableAdapter,您就可以使用命令对象更新数据库中的记录(例如,ExecuteNonQuery)。 有关使用命令对象更新数据的更多信息,请参见下面的“使用命令对象更新记录”。

下表描述了各种 TableAdapter.Update 方法的行为:

表 1

方法

说明

TableAdapter.Update(DataTable)

尝试将 DataTable 中的所有更改保存到数据库中。 (这包括从表中移除所有删除的行、将插入的行添加到表中、更新表中已更改的所有行。)

TableAdapter.Update(DataSet)

虽然该参数带有一个数据集,但 TableAdapter 仍尝试将 TableAdapter 的关联 DataTable 中的所有更改保存到数据库中。 (这包括从表中移除所有删除的行、将插入的行添加到表中、更新表中已更改的所有行。)

注意注意
TableAdapter 的关联 DataTable 是最初配置 TableAdapter 时创建的 DataTable

TableAdapter.Update(DataRow)

尝试将指示 DataRow 中的更改保存到数据库中。

TableAdapter.Update(DataRows())

尝试将 DataRow 数组中任意行中的更改保存到数据库中。

TableAdapter.Update("new column values", "original column values")

尝试保存由原始列值标识的单行中的更改。

通常,当应用程序使用数据集以独占方式存储数据时,您使用的是带有 DataSetDataTable 或 DataRow 的 TableAdapter.Update 方法。

通常,当应用程序使用对象存储数据时,您使用的是带有列值的 TableAdapter.Update 方法。

如果 TableAdapter 没有带列值的 Update 方法,就表示已将 TableAdapter 配置为使用存储过程,或者已将它的 GenerateDBDirectMethods 属性设置为 false。 尝试从“数据集设计器”内将 TableAdapter 的 GenerateDBDirectMethods 属性设置为 true,然后保存该数据集以重新生成 TableAdapter。 如果 TableAdapter 仍没有带列值的 Update 方法,该表就可能没有提供足够多的架构信息以区分各行(例如,未在表中设置任何主键)。

使用 TableAdapter 更新现有记录

根据应用程序的需要,TableAdapter 提供了更新数据库中记录的不同方法。

如果应用程序使用数据集存储数据,则可以在所需的 DataTable 中简单地更新记录,然后调用 TableAdapter.Update 方法并传入 DataSetDataTableDataRow 或 DataRow 数组。 上表描述了不同的 Update 方法。

用带有 DataSet、DataTable、DataRow 或 DataRows() 的 TableAdapter.Update 方法更新数据库中的记录

  1. 通过直接编辑 DataTable 中的 DataRow,编辑所需的 DataTable 中的记录。 有关更多信息,请参见 如何:编辑数据表中的行

  2. 在 DataTable 中对行进行编辑后,请调用 TableAdapter.Update 方法。 通过传入完整的 DataSetDataTableDataRow 数组或单个 DataRow,您可以控制要更新的数据量。

    下面的代码显示如何编辑 DataTable 中的记录,然后调用 TableAdapter.Update 方法将更改保存到数据库中。 (此示例使用 Northwind 数据库 Region 表。)

    C#
    // Locate the row you want to update.
    NorthwindDataSet.RegionRow regionRow;
    regionRow = northwindDataSet.Region.FindByRegionID(1);
    
    // Assign the new value to the desired column.
    regionRow.RegionDescription = "East";
    
    // Save the updated row to the database.
    this.regionTableAdapter.Update(this.northwindDataSet.Region);
    

如果应用程序使用对象存储应用程序中的数据,您就可以使用 TableAdapter 的 DBDirect 方法将数据从对象中直接发送到数据库。 这些方法可让您将各列的单个值传递为方法参数。 调用此方法用传入该方法的列值更新数据库中的现有记录。

以下过程使用 Northwind Region 表作为示例。

使用带有列值的 TableAdapter.Update 方法更新数据库中的记录

  • 调用 TableAdapter 的 Update 方法,以参数的形式为每一列传入新值和原始值。

     提示

    如果没有实例可用,请实例化您要使用的 TableAdapter。

    C#
    NorthwindDataSetTableAdapters.RegionTableAdapter regionTableAdapter = 
        new NorthwindDataSetTableAdapters.RegionTableAdapter();
    
    regionTableAdapter.Update(1, "East", 1, "Eastern");
    

使用命令对象更新记录

下面的示例使用命令对象直接更新数据库中的现有记录。 有关使用命令对象执行命令和存储过程的更多信息,请参见 将数据获取到应用程序

以下过程使用 Northwind Region 表作为示例。

使用命令对象更新数据库中的现有记录

  • 创建新的命令对象;设置它的 Connection、CommandType 和 CommandText 属性;然后打开一个连接,并执行该命令。

    C#
    System.Data.SqlClient.SqlConnection sqlConnection1 = 
        new System.Data.SqlClient.SqlConnection("YOUR CONNECTION STRING");
    
    System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand();
    cmd.CommandType = System.Data.CommandType.Text;
    cmd.CommandText = "UPDATE Region SET [RegionDescription] = @RegionDescription WHERE [RegionID] = @RegionID";
    cmd.Parameters.AddWithValue("@RegionDescription", "East");
    cmd.Parameters.AddWithValue("@RegionID", "1");
    cmd.Connection = sqlConnection1;
    
    sqlConnection1.Open();
    cmd.ExecuteNonQuery();
    sqlConnection1.Close();
    

安全性

您必须具有访问正尝试连接到的数据库的权限,以及更新所需表中记录的权限。


 

如何:删除数据库中的记录

要从数据库中删除记录,请使用 TableAdapter.Update 方法或 TableAdapter.Delete 方法。 或者,如果您的应用程序不使用 TableAdapters,您就可以使用命令对象从数据库中删除记录(例如,ExecuteNonQuery)。

当应用程序使用数据集存储数据时通常使用 TableAdapter.Update 方法,而当应用程序使用对象存储数据时通常会使用 TableAdapter.Delete 方法。

如果 TableAdapter 没有 Delete 方法,则意味着或者该 TableAdapter 是为使用存储过程而配置的,或者其 GenerateDBDirectMethods 属性被设置为 false。 尝试从“数据集设计器”内将 TableAdapter 的 GenerateDBDirectMethods 属性设置为 true,然后保存该数据集以重新生成 TableAdapter。 如果 TableAdapter 仍没有 Delete 方法,那么该表可能没有提供足够的架构信息在单个行之间进行区分(例如,表中未设置主键)。

使用 TableAdapters 删除记录

根据应用程序的需要,TableAdapters 提供了从数据库中删除记录的不同方法。

如果应用程序使用数据集存储数据,则可以直接从 DataSet 中所需的 DataTable 中删除记录,然后调用 TableAdapter.Update 方法。 TableAdapter.Update 方法得到数据表中的所有更改,并将这些更改发送到数据库中(包括插入、更新以及删除记录)。

使用 TableAdapter.Update 方法从数据库中删除记录

  • 通过从表中删除 DataRow 对象,从所需 DataTable 中删除记录。 有关更多信息,请参见如何:删除数据表中的行。 从 DataTable 中删除行后,请调用 TableAdapter.Update 方法。 通过传入完整的 DataSetDataTableDataRow 数组或单个 DataRow,您可以控制要更新的数据量。 下面的代码显示如何从 DataTable 中删除记录,然后调用 TableAdapter.Update 方法传达更改并从数据库中删除行 (此示例使用 Northwind 数据库的 Region 表)。

    C#
    // Locate the row to delete.
    NorthwindDataSet.RegionRow oldRegionRow;
    oldRegionRow = northwindDataSet.Region.FindByRegionID(5);
    
    // Delete the row from the dataset
    oldRegionRow.Delete();
    
    // Delete the row from the database
    this.regionTableAdapter.Update(this.northwindDataSet.Region);
    

如果应用程序使用对象存储应用程序中的数据,您就可以使用 TableAdapter 的 DBDirect 方法直接从数据库中删除数据。 根据传入的参数值,调用 Delete 方法从数据库中移除记录。

使用 TableAdapter.Delete 方法从数据库中删除记录

  • 调用 TableAdapter 的 Delete 方法,为每一列传入值作为 Delete 方法的参数 (此示例使用 Northwind 数据库的 Region 表)。

     提示

    如果没有实例可用,请实例化您要使用的 TableAdapter。

    C#
    NorthwindDataSetTableAdapters.RegionTableAdapter regionTableAdapter = 
        new NorthwindDataSetTableAdapters.RegionTableAdapter();
    
    regionTableAdapter.Delete(5, "NorthWestern");
    

使用命令对象删除记录

下面的示例使用命令对象直接从数据库中删除记录。 有关使用命令对象执行命令和存储过程的更多信息,请参见将数据获取到应用程序

使用命令对象从数据库中删除记录

  • 创建新的命令对象,并设置它的 Connection、CommandType 和 CommandText 属性 (此示例使用 Northwind 数据库的 Region 表)。

    C#
    System.Data.SqlClient.SqlConnection sqlConnection1 = 
        new System.Data.SqlClient.SqlConnection("YOUR CONNECTION STRING ");
    
    System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand();
    cmd.CommandType = System.Data.CommandType.Text;
    cmd.CommandText = "DELETE Region WHERE RegionID = 5 AND RegionDescription = 'NorthWestern'";
    cmd.Connection = sqlConnection1;
    
    sqlConnection1.Open();
    cmd.ExecuteNonQuery();
    sqlConnection1.Close();
    

安全性

您必须具有访问正在尝试连接到的数据库的权限,以及从所需表中删除记录的权限。

如何:使用 TableAdapter 直接访问数据库

除了 InsertCommand、UpdateCommand 和 DeleteCommand 之外,创建 TableAdapter 时还生成了一些可以直接在数据库上执行的方法。 可以直接调用这些方法(TableAdapter.Insert、TableAdapter.Update 和 TableAdapter.Delete)对数据库中的数据进行操作。

如果不想创建这些直接方法,可在**“属性”**窗口中将 TableAdapter 的 GenerateDbDirectMethods 属性设置为 false。 除了 TableAdapter 的主查询之外,所有添加到 TableAdapter 的查询也是独立查询 -- 它们不生成这些 DbDirect 方法。

直接向数据库发送命令

调用执行您尝试完成的任务的 TableAdapter DbDirect 方法。

直接向数据库中插入新记录

  • 调用 TableAdapter 的 Insert 方法,为每一列传入值作为参数。 以下过程使用 Northwind 数据库的 Region 表作为示例。

     提示

    如果没有实例可用,请实例化您要使用的 TableAdapter。

    C#
    NorthwindDataSetTableAdapters.RegionTableAdapter regionTableAdapter = 
        new NorthwindDataSetTableAdapters.RegionTableAdapter();
    
    regionTableAdapter.Insert(5, "NorthWestern");
    

直接在数据库中更新记录

  • 调用 TableAdapter 的 Update 方法,以参数的形式为每一列传入新值和原始值。

     提示

    如果没有实例可用,请实例化您要使用的 TableAdapter。

    C#
    NorthwindDataSetTableAdapters.RegionTableAdapter regionTableAdapter = 
        new NorthwindDataSetTableAdapters.RegionTableAdapter();
    
    regionTableAdapter.Update(1, "East", 1, "Eastern");
    

直接从数据库中删除记录

  • 调用 TableAdapter 的 Delete 方法,为每一列传入值作为 Delete 方法的参数 (此示例使用 Northwind 数据库的 Region 表)。

     提示

    如果没有实例可用,请实例化您要使用的 TableAdapter。

    C#
    NorthwindDataSetTableAdapters.RegionTableAdapter regionTableAdapter = 
        new NorthwindDataSetTableAdapters.RegionTableAdapter();
    
    regionTableAdapter.Delete(5, "NorthWestern");
    

如何:将数据从对象保存到数据库

通过将对象中的值传递到 TableAdapter 的 DBDirect 方法之一(例如,TableAdapter.Insert),可将对象中的数据保存到数据库中。 有关更多信息,请参见 TableAdapter 概述

若要保存对象集合中的数据,请循环通过对象集合(例如,for-next 循环),然后使用 TableAdapter 的 DBDirect 方法之一将每个对象的值发送到数据库中。

默认情况下,DBDirect 方法在 TableAdapter 上创建,能够直接对数据库执行操作。 这些方法可以直接调用,它们不要求 DataSet 或 DataTable 对象来协调更改即可将更新发送到数据库。

 提示

配置 TableAdapter 时,主查询必须提供足够的信息,才能创建 DBDirect 方法。 例如,如果将 TableAdapter 配置为从未定义主键列的表中查询数据,它将不会生成 DBDirect 方法。

表 1

TableAdapter DBDirect 方法

说明

TableAdapter.Insert

向数据库中添加新记录,此方法允许将值作为方法参数传入各个列。

TableAdapter.Update

更新数据库中的现有记录。 Update 方法将原始列值和新列值用作方法参数。 原始值用于定位原始记录,新值用于更新该记录。

通过将 DataSetDataTableDataRow、或 DataRow 数组用作方法参数,TableAdapter.Update 方法还可用于将数据集中的更改协调回数据库。

TableAdapter.Delete

在基于作为方法参数传入的原始列值的数据库中,删除其现有记录。

将对象中的新记录保存到数据库中

  • 通过将值传递给 TableAdapter.Insert 方法可创建这些记录。

    下面的示例通过将 currentCustomer 对象中的值传递给 TableAdapter.Insert 方法,在 Customers 表中创建一项新的客户记录。

    C#
    private void AddNewCustomers(Customer currentCustomer)
    {
        customersTableAdapter.Insert( 
            currentCustomer.CustomerID, 
            currentCustomer.CompanyName, 
            currentCustomer.ContactName, 
            currentCustomer.ContactTitle, 
            currentCustomer.Address, 
            currentCustomer.City, 
            currentCustomer.Region, 
            currentCustomer.PostalCode, 
            currentCustomer.Country, 
            currentCustomer.Phone, 
            currentCustomer.Fax);
    }
    

将对象中的现有记录更新到数据库

  • 修改记录:调用 TableAdapter.Update 方法并传入新值可更新记录,而传入原始值可定位记录。

     提示

    对象需要保留其原始值,才能将它们传递给 Update 方法。 此示例使用前缀为 orig 的属性存储原始值。

    下面的示例通过将 Customer 对象中的新值和原始值传递给 TableAdapter.Update 方法,更新 Customers 表中的现有记录。

    C#
    private void UpdateCustomer(Customer cust)
    {
        customersTableAdapter.Update(
            cust.CustomerID,
            cust.CompanyName,
            cust.ContactName,
            cust.ContactTitle,
            cust.Address,
            cust.City,
            cust.Region,
            cust.PostalCode,
            cust.Country,
            cust.Phone,
            cust.Fax,
            cust.origCustomerID,
            cust.origCompanyName,
            cust.origContactName,
            cust.origContactTitle,
            cust.origAddress,
            cust.origCity,
            cust.origRegion,
            cust.origPostalCode,
            cust.origCountry,
            cust.origPhone,
            cust.origFax);
    }
    

删除数据库中的现有记录

  • 通过调用 TableAdapter.Delete 方法并传入原始值定位记录,可删除记录。

     提示

    对象需要保留其原始值,才能将它们传递给 Delete 方法。 此示例使用前缀为 orig 的属性存储原始值。

    下面的示例通过将 Customer 对象中的原始值传递给 TableAdapter.Delete 方法,删除 Customers 表中的记录。

    C#
    private void DeleteCustomer(Customer cust)
    {
        customersTableAdapter.Delete(
            cust.origCustomerID,
            cust.origCompanyName,
            cust.origContactName,
            cust.origContactTitle,
            cust.origAddress,
            cust.origCity,
            cust.origRegion,
            cust.origPostalCode,
            cust.origCountry,
            cust.origPhone,
            cust.origFax);
    }
    

安全性

您必须具有相应的权限,才能对数据库中的表执行选定的 INSERT、UPDATE 或 DELETE。


 

如何:使用事务保存数据

使用 System.Transactions 命名空间在事务中保存数据。 使用 TransactionScope 对象来参与为您自动管理的事务。

项目不是用对 System.Transactions 程序集的引用创建的,因此您需要向使用事务的项目手动添加引用。

 提示

Windows 2000 和更高版本支持 System.Transactions 命名空间。

实现事务的最简便方法是在 using 语句中实例化 TransactionScope 对象。 (有关更多信息,请参见 Using 语句 (Visual Basic) 和 using 语句(C# 参考)。)using 语句内执行的代码将参与事务。

若要提交事务,请调用作为 using 块中最后语句的 Complete 方法。

若要回滚事务,请在调用 Complete 方法之前,引发异常。

有关更多信息,请参见 演练:在事务中保存数据

添加对 System.Transactions dll 的引用

  1. 从**“项目”菜单中选择“添加引用”**。

  2. 在**“.NET”选项卡(SQL Server 项目的“SQL Server”选项卡)上选择“System.Transactions”,并单击“确定”**。

    对 System.Transactions.dll 的引用被添加到项目中。

在事务中保存数据

  • 添加代码以在包含事务的 using 语句内保存数据。 下面的代码显示如何在 using 语句中创建和实例化 TransactionScope 对象:

    C#
    using (System.Transactions.TransactionScope updateTransaction = 
        new System.Transactions.TransactionScope())
    {
        // Add code to save your data here.
        // Throw an exception to roll back the transaction.
    
        // Call the Complete method to commit the transaction
        updateTransaction.Complete();
    }
    

演练:将数据保存到数据库(多个表)

更新:2011 年 5 月

应用程序开发中最常用方案之一是在 Windows 应用程序窗体中显示数据、编辑数据并将更新后的数据发回数据库。 本演练创建可显示两个相关表的数据的窗体,并演示如何编辑记录和将更改保存回数据库。 此示例使用源自 Northwind 示例数据库的 Customers 和 Orders 表。

通过调用 TableAdapter 的 Update 方法,可以将应用程序中的数据保存回数据库。 当从**“数据源”**窗口拖动项时,为拖放到窗体上的第一个表自动添加保存数据的代码。 添加到窗体的任何其他表都要求手动添加保存数据所需的所有代码。 本演练演示如何添加从多个表保存更新的代码。

 提示

显示的对话框和菜单命令可能会与“帮助”中的描述不同,具体取决于您现用的设置或版本。 若要更改设置,请在“工具”菜单上选择“导入和导出设置”。 有关更多信息,请参见 使用设置

本演练涉及以下任务:

系统必备

若要完成本演练,您需要:

创建 Windows 应用程序

第一步是创建**“Windows 应用程序”**。 在此步骤中为项目指定名称是可选的,但由于我们打算稍后保存该项目,因此为它指定了一个名称。

创建新的 Windows 应用程序项目

  1. 从**“文件”**菜单创建一个新的项目。

  2. 将项目命名为 UpdateMultipleTablesWalkthrough。

  3. 选择**“Windows 应用程序”,然后单击“确定”**。 有关更多信息,请参见 创建基于 Windows 的应用程序

    UpdateMultipleTablesWalkthrough 项目即被创建并添加到**“解决方案资源管理器”**中。

创建数据源

此步骤使用**“数据源配置向导”**从 Northwind 数据库创建一个数据源。 您必须具有访问 Northwind 示例数据库的权限,才能创建连接。 有关设置 Northwind 示例数据库的信息,请参见 如何:安装示例数据库

创建数据源

  1. 在**“数据”菜单上,单击“显示数据源”**。

  2. 在**“数据源”窗口中,单击“添加新数据源”,启动“数据源配置向导”**。

  3. 在**“选择数据源类型”页上选择“数据库”,然后单击“下一步”**。

  4. 在**“选择您的数据连接”**页面上执行下列操作之一:

    • 如果下拉列表中包含到 Northwind 示例数据库的数据连接,请选择该连接。

      - 或 -

    • 选择**“新建连接”,打开“添加/修改连接”**对话框。 有关更多信息,请参见 “添加/修改连接”对话框(通用)

  5. 如果数据库需要密码,请选择该选项以包括敏感数据,再单击**“下一步”**。

  6. 在**“将连接字符串保存到应用程序配置文件”页面上单击“下一步”**。

  7. 在**“选择数据库对象”页面上展开“表”**节点。

  8. 选择**“Customers”和“Orders”表,然后单击“完成”**。

    **“NorthwindDataSet”被添加到您的项目,这些表将出现在“数据源”**窗口中。

设置要创建的控件

对于本演练,Customers 表中的数据将处于**“详细信息”布局(数据在单独控件中显示)。 来自 Orders 表的数据将处于“网格”**布局(在 DataGridView 控件中显示)。

设置数据源窗口中项的放置类型

  1. 在**“数据源”窗口中展开“Customers”**节点。

  2. 通过从**“Customers”节点上的控件列表中选择“详细信息”,将“Customers”**表中的控件更改为单独的控件。 有关更多信息,请参见 如何:设置从“数据源”窗口中拖动时要创建的控件

创建数据绑定窗体

可以通过将某些项从**“数据源”**窗口拖到您的窗体上来创建数据绑定控件。

在窗体上创建数据绑定控件

  1. 将主**“Customers”节点从“数据源”窗口拖到“Form1”**上。

    带有描述性标签的数据绑定控件会出现在窗体上,同时还显示一个工具条 (BindingNavigator),用于在记录间进行导航。 组件栏中出现 NorthwindDataSetCustomersTableAdapterBindingSource 和 BindingNavigator

  2. 将相关的**“Orders”节点从“数据源”窗口拖动到“Form1”**上。

     提示

    相关的“Orders”节点位于“Fax”列下,该节点是“Customers”节点的子节点。

    用于导航记录的 DataGridView 控件和工具条 (BindingNavigator) 出现在窗体上。 OrdersTableAdapter 和 BindingSource 出现在组件栏中。

添加用于更新数据库的代码

通过调用**“Customers”和“Orders”TableAdapter 的 Update 方法可以更新数据库。 默认情况下,BindingNavigator 的“保存”**按钮的事件处理程序被添加到窗体代码中,以将更新内容发送到数据库。 本过程将修改该代码以按正确的顺序发送更新内容,从而消除产生引用完整性错误的可能性。 该代码还将通过在 try-catch 块中包装更新调用来实现错误处理。 可以根据应用程序的需要修改代码。

 提示

为了清楚起见,本演练未使用事务,但是如果您要更新两个或多个相关表,则应在一个事务中包含所有更新逻辑。 事务是指一个过程,它首先确保对数据库的所有相关更改均可成功完成,然后再提交更改。 有关更多信息,请参见 事务和并发 (ADO.NET)

向应用程序添加更新逻辑

  1. 在 BindingNavigator 上双击**“保存”**按钮,打开代码编辑器并转到 bindingNavigatorSaveItem_Click 事件处理程序。

  2. 替换事件处理程序中的代码以调用相关 TableAdapter 的 Update 方法。 下面的代码首先创建三个临时数据表以保存每个 DataRowState 的更新信息(DeletedAdded 和 Modified)。 然后,按正确的顺序执行更新。 代码应类似于:

    C#
    this.Validate();
    this.ordersBindingSource.EndEdit();
    this.customersBindingSource.EndEdit();
    
    NorthwindDataSet.OrdersDataTable deletedOrders = (NorthwindDataSet.OrdersDataTable)
        northwindDataSet.Orders.GetChanges(DataRowState.Deleted);
    
    NorthwindDataSet.OrdersDataTable newOrders = (NorthwindDataSet.OrdersDataTable)
        northwindDataSet.Orders.GetChanges(DataRowState.Added);
    
    NorthwindDataSet.OrdersDataTable modifiedOrders = (NorthwindDataSet.OrdersDataTable)
        northwindDataSet.Orders.GetChanges(DataRowState.Modified);
    
    try
    {
        // Remove all deleted orders from the Orders table.
        if (deletedOrders != null)
        {
            ordersTableAdapter.Update(deletedOrders);
        }
    
        // Update the Customers table.
        customersTableAdapter.Update(northwindDataSet.Customers);
    
        // Add new orders to the Orders table.
        if (newOrders != null)
        {
            ordersTableAdapter.Update(newOrders);
        }
    
        // Update all modified Orders.
        if (modifiedOrders != null)
        {
            ordersTableAdapter.Update(modifiedOrders);
        }
    
        northwindDataSet.AcceptChanges();
    }
    
    catch (System.Exception ex)
    {
        MessageBox.Show("Update failed");
    }
    
    finally
    {
        if (deletedOrders != null)
        {
            deletedOrders.Dispose();
        }
        if (newOrders != null)
        {
            newOrders.Dispose();
        }
        if (modifiedOrders != null)
        {
            modifiedOrders.Dispose();
        }
    }
    

测试应用程序

测试应用程序

  1. 按 F5。

  2. 对每个表中的一条或多条记录的数据执行一些更改。

  3. 按**“保存”**按钮。

  4. 检查数据库中的值以验证更改已保存。

     提示

    有关演示如何在更改 Customers 或添加新 Customer 和 Orders 数据后更新 Orders 列表的示例代码,请参见如何:将新记录插入数据库演练:保存相关数据表中的数据(分层更新)

后续步骤

根据应用程序的要求,在 Windows 应用程序中创建了绑定数据窗体后,还需要执行一些步骤。 您可以通过以下操作来增强此演练的效果:


 

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章