使用 ADO.NET 的批处理操作

Posted

技术标签:

【中文标题】使用 ADO.NET 的批处理操作【英文标题】:Batch operations using ADO.NET 【发布时间】:2019-12-13 18:13:17 【问题描述】:

我正在尝试使用 ADO.NET 进行批量插入。到目前为止,我发现使用SqlDataAdapter 可能是一种方法。

我已经创建了包含 1 个表和 3 列的数据库以及将向该表中插入行的存储过程。

PropertyTable:

Name: VARCHAR(10)
Number: INT
IsActive: BIT

存储过程InsertPropertyTable:

CREATE PROCEDURE InsertPropertyTable
    @Par1 VARCHAR(10),
    @Par2 INT,
    @Par3 BIT
AS
BEGIN
    INSERT INTO dbo.PropertyTable
    VALUES (@Par1, @Par2, @Par3)
END
GO

C#代码:

namespace InsertManyTest.Console

    using System;
    using System.Data;
    using System.Data.SqlClient;

    class Program
    
        static void Main(string[] args)
        
            var connString = "Server=.;Database=Test;Trusted_Connection=True;";
            using (var connection = new SqlConnection(connString))
            
                var adapter = new SqlDataAdapter("", connection);

                var dataTable = new DataTable();
                dataTable.Columns.AddRange(new[]
                
                    new DataColumn("Par1", typeof(string)),
                    new DataColumn("Par2", typeof(int)),
                    new DataColumn("Par3", typeof(bool))
                );

                adapter.InsertCommand = new SqlCommand("InsertPropertyTable", connection);
                adapter.InsertCommand.CommandType = CommandType.StoredProcedure;
                adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.None;

                adapter.DeleteCommand = new SqlCommand();
                adapter.DeleteCommand.UpdatedRowSource = UpdateRowSource.None;
                adapter.UpdateCommand = new SqlCommand();
                adapter.UpdateCommand.UpdatedRowSource = UpdateRowSource.None;
                adapter.SelectCommand = new SqlCommand();
                adapter.SelectCommand.UpdatedRowSource = UpdateRowSource.None;

                adapter.InsertCommand.Parameters.Add("@Par1", SqlDbType.VarChar,10, "Par1");
                adapter.InsertCommand.Parameters.Add("@Par2", SqlDbType.Int, 1, "Par2");
                adapter.InsertCommand.Parameters.Add("@Par3", SqlDbType.Bit, 1, "Par3");

                CreateRow(dataTable);
                CreateRow(dataTable);
                CreateRow(dataTable);
                CreateRow(dataTable);

                adapter.UpdateBatchSize = 2;
                adapter.Update(dataTable);
            
        

        private static void CreateRow(DataTable dataTable)
        
            var row = dataTable.NewRow();
            FillRow(row);
            dataTable.Rows.Add(row);
        

        private static void FillRow(DataRow row)
        
            row["Par1"] = "Asd";
            row["Par2"] = 1;
            row["Par3"] = true;
        
    

UpdateBatchSize 设置为默认值 (1) 时,它可以工作,但每次插入都会进行往返。我想避免这种情况,所以我将UpdateBatchSize 设置为更大的值,但随后我得到了一些奇怪的异常:

System.ArgumentException: '指定的参数名称'Parameter1' 无效。'

来自adaper.Update(...);

我预计此更新会生成如下内容:

sp_exec InsertPropertyTable 'Asd', 1;
sp_exec InsertPropertyTable 'Asd', 1;
sp_exec InsertPropertyTable 'Asd', 1;
sp_exec InsertPropertyTable 'Asd', 1;

将在单次往返数据库中执行


更新: 我在堆栈溢出中发现了这个问题,作者正在对我做类似的事情。在他的情况下,它正在工作,他与其他批量插入的方法进行了比较

Bulk insert using SP

Blog post from 2008


更新:

我是个笨笨笨的笨蛋。我在 .net core 2.2 中测试了这个,而不是在 .net 4.6.1 上。 它不适用于 .net core 2.2,但它适用于 .net 4.6.1。

我没有看到预期的结果,因为 sql server profiler 中有几行。每行包含一个exec ...

【问题讨论】:

你确定在索引到DataRow时参数名前面应该有@吗? @PalleDue 与 UpdateBatchSize=1 一起使用,只要我更改它就会停止工作。 【参考方案1】:

似乎您可能会使用表值参数。

基本上,您在 SQL Server 端创建新的自定义类型,它反映了您想要发送到存储过程的数据结构(一行)。然后,您使用该类型来通知您的存储过程接受的参数类型。在存储过程中接收到该表值参数后,您可以像查询任何其他表一样查询该表值参数:例如,从中选择所有内容并插入到您的真实表中。

ref1:https://docs.microsoft.com/en-us/sql/relational-databases/tables/use-table-valued-parameters-database-engine?view=sql-server-2017

ref2:https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/table-valued-parameters

【讨论】:

我知道UserDefinedType 会起作用,但要做到这一点需要付出额外的努力。此外,如果你想插入一行,你必须假装你有一个集合或制作单独的存储过程

以上是关于使用 ADO.NET 的批处理操作的主要内容,如果未能解决你的问题,请参考以下文章

您如何确定现实世界中推荐的 ADO.NET 批量大小?

深入详解DataTable

如何使用 ADO 在 C# 中获得高效的 Sql Server 死锁处理?

ADO.Net——扩展属性和配置文件应用

c#编写的代码用:linq和传统的ado.net哪个好,出错少,哪个更快,对服务器的负担最小

添加 ADO.NET 实体模型崩溃