验证 .NET Framework 应用程序中数据集内的数据

注意

数据集和相关类是 2000 年代初的旧 .NET Framework 技术,使应用程序能够在应用程序与数据库断开连接时处理内存中的数据。 它们对于使用户能够修改数据并持续更改回数据库的应用程序特别有用。 虽然数据集已被证明是一项非常成功的技术,但我们建议新的 .NET 应用程序使用 Entity Framework Core。 实体框架提供了一种更自然的方式来将表格数据作为对象模型,并且具有更简单的编程接口。

验证数据是确认输入到数据对象中的值符合数据集架构内的约束的过程。 验证过程还会确认这些值遵循已为应用程序建立的规则。 在将更新发送到基础数据库之前对数据进行验证是一种很好的做法。 这可以减少错误,还能减少应用程序和数据库之间的潜在往返行程次数。

可以通过在数据集本身中构建验证检查,来确认写入数据集的数据是有效的。 无论如何执行更新(无论是通过表单中的控件直接更新、在组件中更新,还是通过其他方式更新),数据集都可以检测数据。 由于数据集是应用程序的一部分(与数据库后端不同),因此在其中构建特定于应用程序的验证是符合逻辑的。

向应用程序添加验证的最佳位置是数据集的分部类文件中。 在 Visual Basic 或 Visual C# 中打开数据集设计器,然后双击要为其创建验证的列或表。 此操作将打开代码文件,你可在该文件中创建 ColumnChangingRowChanging 事件处理程序。

private static void OnColumnChanging(object sender, DataColumnChangeEventArgs e)
{

}

验证数据

数据集内的验证通过以下方式完成:

当记录中发生更改时,DataTable 对象会引发多个事件:

  • 每次更改单个列时和更改之后都会引发 ColumnChangingColumnChanged 事件。 要验证特定列中的更改时,ColumnChanging 事件很有用。 有关建议的更改的信息将作为参数传递到事件。
  • 行中发生任何更改时和更改后,将引发 RowChangingRowChanged 事件。 RowChanging 事件更常见。 它表示行中某个位置正在发生更改,但不知道哪个列发生了更改。

默认情况下,对列的每个更改都将引发四个事件。 首先是更改的特定列的 ColumnChangingColumnChanged 事件。 接下来是 RowChangingRowChanged 事件。 如果对行进行了多项更改,则每次发生更改时都会引发事这些件。

注意

每个列发生更改后,数据行的 BeginEdit 方法会关闭 RowChangingRowChanged 事件。 在这种情况下,在调用 EndEdit 方法之前,如果仅引发一次 RowChangingRowChanged 事件,则不会引发事件。 有关详细信息,请参阅在填充数据集时关闭约束

你选择的事件取决于你希望的验证粒度。 如果在列发生更改时立即捕获错误很重要,请使用 ColumnChanging 事件生成验证。 否则,请使用 RowChanging 事件,这可能会导致同时捕获几个错误。 此外,如果你的数据是结构化的,以便根据另一列的内容来验证一列的值,请在 RowChanging 事件期间执行验证。

更新记录后,DataTable 对象会引发事件,你可以在发生更改时和进行更改后响应这些事件。

如果你的应用程序使用类型化数据集,则可以创建强类型化事件处理程序。 这将添加你可为其创建处理程序的四个其他类型化事件:dataTableNameRowChangingdataTableNameRowChangeddataTableNameRowDeletingdataTableNameRowDeleted。 这些类型化事件处理程序会传递一个参数,该参数包含表的列名,使代码更易于编写和读取。

数据更新事件

事件 说明
ColumnChanging 列中的值正在更改。 事件向你传递行和列以及建议的新值。
ColumnChanged 列中的值已更改。 事件向你传递行和列以及建议的值。
RowChanging DataRow 对象的更改将提交回数据集。 如果未调用 BeginEdit 方法,则在引发 ColumnChanging 事件后,对列的每次更改后立即引发 RowChanging 事件。 如果在进行更改之前调用 BeginEdit,则仅在调用 EndEdit 方法时引发 RowChanging 事件。

事件将行传递给你,并提供一个值,该值指示正在执行的操作类型(更改、插入等)。
RowChanged 已更改行。 事件将行传递给你,并提供一个值,该值指示正在执行的操作类型(更改、插入等)。
RowDeleting 正在删除行。 事件将行传递给你,并提供一个值,该值指示正在执行的操作类型(删除)。
RowDeleted 已删除行。 事件将行传递给你,并提供一个值,该值指示正在执行的操作类型(删除)。

在更新过程中会引发 ColumnChangingRowChangingRowDeleting 事件。 可以使用这些事件来验证数据或执行其他类型的处理。 由于在这些事件期间正在进行更新,因此你可以通过引发异常来取消更新,这会阻止更新完成。

ColumnChangedRowChangedRowDeleted 事件是更新成功完成后引发的通知事件。 如果希望根据成功的更新执行进一步的操作,这些事件很有用。

在列更改过程中验证数据

注意

数据集设计器会创建分部类,可在其中将验证逻辑添加到数据集。 设计器生成的数据集不会删除或更改分部类中的任何代码。

数据列中的值更改时,可以通过响应 ColumnChanging 事件来验证数据。 引发此事件时,它会传递事件参数 (ProposedValue),其中包含为当前列建议的值。 根据 e.ProposedValue 的内容,可以执行以下操作:

  • 不执行任何操作,接受建议的值。

  • 从更改列的事件处理程序中设置列错误 (SetColumnError)以拒绝建议的值。

  • 可以使用 ErrorProvider 控件向用户显示错误消息。 有关详细信息,请参阅 ErrorProvider 组件

RowChanging 事件期间也可以执行验证。

在行更改过程中验证数据

可以编写代码以验证你要验证的每个列是否包含满足应用程序要求的数据。 设置列,使其在建议的值不可接受时包含错误,以完成此操作。 下面的示例在 Quantity 列等于或小于 0 时设置列错误。 更改行的事件处理程序应类似于以下示例。

在行更改时验证数据 (Visual Basic)

  1. 在“数据集设计器”中打开数据集。 有关详细信息,请参阅演练:在数据集设计器中创建数据集

  2. 双击要验证的表的标题栏。 此操作会在数据集的分部类文件中自动创建 DataTableRowChanging 事件处理程序。

    提示

    双击表名称的左侧,以创建更改行的事件处理程序。 如果双击表名,你可以对其进行编辑。

    Private Sub Order_DetailsDataTable_Order_DetailsRowChanging(
        ByVal sender As System.Object, 
        ByVal e As Order_DetailsRowChangeEvent
      ) Handles Me.Order_DetailsRowChanging
    
        If CType(e.Row.Quantity, Short) <= 0 Then
            e.Row.SetColumnError("Quantity", "Quantity must be greater than 0")
        Else
            e.Row.SetColumnError("Quantity", "")
        End If
    End Sub
    

在行更改时验证数据 (C#)

  1. 在“数据集设计器”中打开数据集。 有关详细信息,请参阅演练:在数据集设计器中创建数据集

  2. 双击要验证的表的标题栏。 此操作会为 DataTable 创建分部类文件。

    注意

    数据集设计器不会自动为 RowChanging 事件创建事件处理程序。 必须创建处理 RowChanging 事件的方法并运行代码,以在表的初始化方法中连接该事件。

  3. 将以下代码复制到分部类:

    public override void EndInit()
    {
        base.EndInit();
        Order_DetailsRowChanging += TestRowChangeEvent;
    }
    
    public void TestRowChangeEvent(object sender, Order_DetailsRowChangeEvent e)
    {
        if ((short)e.Row.Quantity <= 0)
        {
            e.Row.SetColumnError("Quantity", "Quantity must be greater than 0");
        }
        else
        {
            e.Row.SetColumnError("Quantity", "");
        }
    }
    

检索已更改的行

数据表中的每一行都有 RowState 属性,该属性使用 DataRowState 枚举中的值跟踪该行的当前状态。 可以通过调用 DataSetDataTableGetChanges 方法,从数据集或数据表返回已更改的行。 可以通过调用数据集的 HasChanges 方法来验证在调用 GetChanges 之前是否存在更改。

注意

(通过调用 AcceptChanges 方法)提交对数据集或数据表的更改后,GetChanges 方法不会返回任何数据。 如果应用程序需要处理已更改的行,则必须在调用 AcceptChanges 方法之前处理这些更改。

调用数据集或数据表的 GetChanges 方法将返回仅包含已更改记录的新数据集或数据表。 如果要获取特定记录(例如仅新记录或仅经修改的记录),可以将来自 DataRowState 枚举的值作为参数传递给 GetChanges 方法。

使用 DataRowVersion 枚举访问行的其他版本(例如,处理行之前存在于行中的原始值)。

从数据集获取所有已更改的记录

  • 调用数据集的 GetChanges 方法。

    下面的示例会创建名为 changedRecords 的新数据集,并用另一个名为 dataSet1 的数据集中的所有已更改记录填充它。

    DataSet changedRecords = dataSet1.GetChanges();
    

从数据表获取所有已更改的记录

  • 调用 DataTable 的 GetChanges 方法。

    下面的示例将创建名为 changedRecordsTable 的新数据表,并使用另一个名为 dataTable1 的数据表中已更改的所有记录填充它。

    DataTable changedRecordsTable = dataTable1.GetChanges();
    

获取具有特定行状态的所有记录

  • 调用数据集或数据表的 GetChanges 方法,并以参数的形式传递 DataRowState 枚举值。

    下面的示例演示如何创建名为 addedRecords 的新数据集,并使用已添加到 dataSet1 数据集的记录来填充它。

    DataSet addedRecords = dataSet1.GetChanges(DataRowState.Added);
    

    下面的示例演示如何返回最近添加到 Customers 表中的所有记录:

    private NorthwindDataSet.CustomersDataTable GetNewRecords()
    {
        return (NorthwindDataSet.CustomersDataTable)
            northwindDataSet1.Customers.GetChanges(DataRowState.Added);
    }
    

访问 DataRow 的原始版本

对数据行进行更改时,数据集将保留该行的原始版本 (Original) 和新版本 (Current)。 例如,在调用 AcceptChanges 方法之前,应用程序可以访问记录的不同版本(如 DataRowVersion 枚举中所定义),并相应地处理更改。

注意

行的不同版本仅在编辑行后、调用 AcceptChanges 方法之前存在。 调用 AcceptChanges 方法后,当前版本和原始版本相同。

传递 DataRowVersion 值连同列索引(或列名称作为字符串)将从该列的特定行版本返回值。 在 ColumnChangingColumnChanged 事件期间,会标识已更改的列。 这是检查行的不同版本以进行验证的好时机。 但是,如果你暂时挂起了约束,则不会引发这些事件,将需要以编程方式标识哪些列已更改。 为此,可以循环访问 Columns 集合并比较不同的 DataRowVersion 值。

获取记录的原始版本

  • 通过传入要返回的行的 DataRowVersion 来访问列的值。

    下面的示例演示如何使用 DataRowVersion 值获取 DataRowCompanyName 字段的原始值:

    string originalCompanyName;
    originalCompanyName = northwindDataSet1.Customers[0]
        ["CompanyName", DataRowVersion.Original].ToString();
    

访问 DataRow 的当前版本

获取记录的当前版本

  • 访问列的值,然后向索引添加一个参数,用于指示要返回行的哪个版本。

    下面的示例演示如何使用 DataRowVersion 值获取 DataRowCompanyName 字段的当前值:

    string currentCompanyName;
    currentCompanyName = northwindDataSet1.Customers[0]
        ["CompanyName", DataRowVersion.Current].ToString();