使用 DataAdapter 的批量操作

适用于 .NET Framework .NET .NET Standard

下载 ADO.NET

ADO.NET 中的批处理支持允许DataAdapter将来自DataSetDataTable的INSERT、UPDATE和DELETE操作分组后发送到服务器,而不是一次只发送一个操作。 到服务器的往返次数减少通常会带来显著的性能提升。 适用于 SQL Server 的 Microsoft SqlClient 数据提供程序 (Microsoft.Data.SqlClient) 支持批量更新。

在早期版本的 ADO.NET 中,使用来自 DataSet 的更改更新数据库时,DataAdapterUpdate 方法每次向数据库更新一行。 当该方法循环访问指定 DataTable 中的各行时,它会检查每个 DataRow 以查看其是否已被修改。 如果行已被修改,它会调用相应的 UpdateCommandInsertCommandDeleteCommand,具体取决于该行的 RowState 属性值。 每行更新都需要通过网络往返访问一次数据库。

在 Microsoft SqlClient Data Provider for SQL Server 中,SqlDataAdapter 公开了 UpdateBatchSize 属性。 将 UpdateBatchSize 设置为正整数值可使对数据库的更新以指定大小的批处理形式发送。 例如,将 UpdateBatchSize 设置为 10 可将 10 个单独的语句编成一组并作为单个批处理进行提交。 将 UpdateBatchSize 设置为 0 可使 SqlDataAdapter 使用服务器能够处理的最大批大小。 将其设置为 1 会禁用批量更新,因为每次只发送一行。

注意

执行极大的批处理会降低性能。 因此,在实现应用程序前应进行测试以得到最佳的批大小。

使用 UpdateBatchSize 属性

启用批量更新时,DataAdapter 的 UpdateCommandInsertCommandDeleteCommandUpdatedRowSource 属性值应设置为 NoneOutputParameters。 执行批处理更新时,命令的 UpdatedRowSourceFirstReturnedRecordBoth 属性值无效。

下面的过程演示 UpdateBatchSize 属性的用法。 该过程采用两个自变量,一个是 DataSet 对象,它具有表示 Production.ProductCategory 表中 ProductCategoryID 和 Name 字段的列;另一个是表示批大小的整数(批处理中的行数) 。 代码创建一个新的 SqlDataAdapter 对象,并设置其 UpdateCommandInsertCommandDeleteCommand 属性。 代码假定 DataSet 对象具有经过修改的行。 它设置 UpdateBatchSize 属性并执行更新。

public static void BatchUpdate(DataTable dataTable, Int32 batchSize)
{
    // Assumes GetConnectionString() returns a valid connection string.
    string connectionString = GetConnectionString();

    // Connect to the AdventureWorks database.
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        // Create a SqlDataAdapter.  
        SqlDataAdapter adapter = new SqlDataAdapter();

        // Set the UPDATE command and parameters.  
        adapter.UpdateCommand = new SqlCommand(
            "UPDATE Production.ProductCategory SET "
            + "Name=@Name WHERE ProductCategoryID=@ProdCatID;",
            connection);
        adapter.UpdateCommand.Parameters.Add("@Name",
        SqlDbType.NVarChar, 50, "Name");
        adapter.UpdateCommand.Parameters.Add("@ProdCatID",
        SqlDbType.Int, 4, "ProductCategoryID");
        adapter.UpdateCommand.UpdatedRowSource = UpdateRowSource.None;

        // Set the INSERT command and parameter.  
        adapter.InsertCommand = new SqlCommand(
            "INSERT INTO Production.ProductCategory (Name) VALUES (@Name);",
            connection);
        adapter.InsertCommand.Parameters.Add("@Name",
        SqlDbType.NVarChar, 50, "Name");
        adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.None;

        // Set the DELETE command and parameter.  
        adapter.DeleteCommand = new SqlCommand(
            "DELETE FROM Production.ProductCategory "
            + "WHERE ProductCategoryID=@ProdCatID;", connection);
        adapter.DeleteCommand.Parameters.Add("@ProdCatID",
        SqlDbType.Int, 4, "ProductCategoryID");
        adapter.DeleteCommand.UpdatedRowSource = UpdateRowSource.None;

        // Set the batch size.  
        adapter.UpdateBatchSize = batchSize;

        // Execute the update.  
        adapter.Update(dataTable);
    }
}

DataAdapter 包含两个与更新有关的事件:RowUpdating 和 RowUpdated 。 有关详细信息,请参阅处理 DataAdapter 事件

批量更新时的事件行为变化

启用批处理时,在单个数据库操作中可更新多行。 因此,每个批处理只发生一次 RowUpdated 事件,而对于处理每一行,RowUpdating 事件都会发生。 禁用批处理时,这两个事件一对一交错触发,即一行触发一个 RowUpdating 事件和一个 RowUpdated 事件,下一行触发一个 RowUpdating 事件和一个 RowUpdated 事件,直到处理完所有行。

访问已更新的行

禁用批处理时,可以使用 Row 类的 RowUpdatedEventArgs 属性访问要进行更新的行。

启用批处理时,会为多行生成单个 RowUpdated 事件。 因此,每一行的 Row 属性值为空。 但仍会为每一行生成 RowUpdating 事件。 RowUpdatedEventArgs 类的 CopyToRows 方法允许您通过将这些行的引用复制到数组中来访问已处理的行。 如果没有任何行正在被处理,CopyToRows 会抛出 ArgumentNullException。 在调用 RowCount 方法之前,使用 CopyToRows 属性可返回已处理行的数目。

处理数据错误

执行批处理与执行每个单独的语句具有相同的效果。 各语句按照它们被添加到批处理中的顺序执行。 在批处理模式下处理错误的方式与禁用批处理模式时相同。 每一行均单独处理。 只有在数据库中经过成功处理的行才能在 DataRow 内的相应 DataTable 中更新。

注意

Microsoft SqlClient Data Provider for SQL Server 和后端数据库服务器确定哪些 SQL 构造支持执行批处理。 如果提交执行的是不受支持的语句,则可能抛出异常。

另请参阅