如何使用 sql Bulkcopy 在表中插入 datagridview 行

Posted

技术标签:

【中文标题】如何使用 sql Bulkcopy 在表中插入 datagridview 行【英文标题】:how to use sql Bulkcopy to insert datagridview rows in table 【发布时间】:2015-10-22 23:38:09 【问题描述】:

我想将 datagridview 行插入到 sql server 表中... 现在使用此代码在表中插入来自 DataGridView 的行值:

private DataTable GetDataTableFromDGV(DataGridView dgv)
        
            var dt = new DataTable();
            foreach (DataGridViewColumn column in dgv.Columns)
            
                if (column.Visible)
                
                    dt.Columns.Add();
                
            
            object[] cellValues = new object[dgv.Columns.Count];
            foreach (DataGridViewRow row in dgv.Rows)
            
                for (int i = 0; i < row.Cells.Count; i++)
                
                    cellValues[i] = row.Cells[i].Value;
                
                dt.Rows.Add(cellValues);
            
            return dt;
        

        private void InsertDTtoDB(string ConnectionString, string TableName, DataGridView DGV)
        
            DataTable dt = new DataTable();
            dt = GetDataTableFromDGV(DGV);
            using (SqlConnection cn = new SqlConnection(ConnectionString))
            
                cn.Open();
                using (SqlBulkCopy copy = new SqlBulkCopy(cn))
                                        
                    copy.ColumnMappings.Add(0, 1);
                    copy.ColumnMappings.Add(1, 2);
                    copy.ColumnMappings.Add(2, 3);
                    copy.ColumnMappings.Add(3, 4);
                    copy.ColumnMappings.Add(4, 5);
                    copy.DestinationTableName = TableName;
                    copy.WriteToServer(dt);
                
            
        

但是插入数据有问题: 来自 datatgridview 的空行(启用添加行)存储在最后一个表行中! 请注意图片帮助:

【问题讨论】:

你检查过dtGetDataTableFromDGV返回的数据表内容吗? 谢谢 .. 我是怎么做到的? return dt;上使用断点并先检查其内容。 帮助...我做错了! 没人回答吗? 【参考方案1】:

首先,一些一般性观察可以帮助您更好地理解用 C# 编写代码:

// this line is declaring a new variable named 'dt' 
// and assigning it an instance of a new DataTable
DataTable dt = new DataTable();
// this line is immediately overwriting the new DataTable 
// assigned to 'dt' with the DataTable returned from 'GetDataTableFromDGV'
dt = GetDataTableFromDGV(DGV);

上面应该写声明变量然后赋值:

DataTable dt = GetDataTableFromDGV(DBV);

来自GetDataTableFromDGV 的以下内容将仅在列可见时将列添加到 DataTable:

foreach (DataGridViewColumn column in dgv.Columns)

    if (column.Visible)
    
        dt.Columns.Add();
    
 

然后在InsertDTtoDB 中使用上面的 DataTable 的结果,这显然期望在预定义的序数位置有一组列数:

copy.ColumnMappings.Add(0, 1);
// etc...

以上内容在GetDataTableFromDGV 中创建的DataTable 之间创建了一个非常脆弱的关系,这要求Column 可见,然后在InsertDTtoDB 中使用。

DataTable 和 SqlBulkCopy.ColumnMappings 之间不要使用脆弱的序数关系,而是使用字段的名称创建 DataTable,然后映射到 SqlBulkCopy。 `GetDataTableFromDGV'中的foreach,则变为:

// assumes the columns in the DataGridView are named as follows:
var columns = new[] "Id", "Invoice_Id", "Software_Id", "Price", "Quantity", "Sum" 
var dt = new DataTable();
foreach (DataGridViewColumn column in dgv.Columns)

    // note: this is case-sensitive
    if (columns.Contains(column.Name))
    
        dt.Columns.Add();
    

然后InsertDTtoDB 中的 ColumnMappings 变为:

copy.ColumnMappings.Add("Invoice_Id", "DestinationCol1");
copy.ColumnMappings.Add("Software_Id", "DestinationCol2");
// etc...

将行添加到 GetDataTableFromDGV 中的 DataTable 时,cellValues 变量在 foreach 循环之外声明,但在内部 for 循环中不断覆盖这些值。与其将对象数组添加到 DataTable,不如使用实际的列名来构建 DataRow 并将新的 DataRow 添加到 DataTable。

考虑到上述情况,GetDataTableFromDGV 方法则变为:

private DataTable GetDataTableFromDGV(DataGridView dgv)

    // use your actual columns names instead of Col1, Col2, etc...
    var columns = new[] "Invoice_Id", "Software_Id", "Price", "Quantity", "Sum" 
    var dt = new DataTable();
    foreach (DataGridViewColumn column in dgv.Columns)
    
        if (columns.Contains(column.Name))
        
            dt.Columns.Add();
        
    

    foreach (DataGridViewRow row in dgv.Rows)
    
        DataRow newRow = dt.NewRow();
        foreach (string columnName in columns)
        
            newRow[columnName] = row.Cells[columnName].Value
        

        dt.Rows.Add(newRow);
    
    return dt;

然后InsertDTtoDB 方法变成:

private void InsertDTtoDB(string ConnectionString, string TableName, DataGridView DGV)

    DataTable dt = GetDataTableFromDGV(DGV);
    using (SqlConnection cn = new SqlConnection(ConnectionString))
    
        cn.Open();
        using (SqlBulkCopy copy = new SqlBulkCopy(cn))
           
            // update the "DestinationCol[x]" values to the destination column names
            copy.ColumnMappings.Add("Invoice_Id", "DestinationCol1");
            copy.ColumnMappings.Add("Software_Id", "DestinationCol2");
            copy.ColumnMappings.Add("Price", "DestinationCol3");
            copy.ColumnMappings.Add("Quantity", "DestinationCol4");
            copy.ColumnMappings.Add("Sum", "DestinationCol5");
            copy.DestinationTableName = TableName;
            copy.WriteToServer(dt);
        
    

要回答您关于为什么将空行添加到数据库的问题,我怀疑这是因为 DataGridView 中有空值。假设是这种情况,则将 DataGridView 中的空值排除在添加到 DataTable 中。我会关闭其中一个关键列,例如“Invoice_Id”; GetDataTableFromDGV 方法则变为:

private DataTable GetDataTableFromDGV(DataGridView dgv)

    // assumes the columns in the DataGridView are named as follows:
    var columns = new[] "Invoice_Id", "Software_Id", "Price", "Quantity", "Sum" 
    var dt = new DataTable();
    foreach (DataGridViewColumn column in dgv.Columns)
    
        if (columns.Contains(column.Name))
        
            dt.Columns.Add();
        
    

    foreach (DataGridViewRow row in dgv.Rows)
    
        // check if the row has a null "Invoice_Id" and exclude
        if(row.Cells["Invoice_Id"] == null
        
            continue;
        

        DataRow newRow = dt.NewRow();
        foreach (string columnName in columns)
        
            newRow[columnName] = row.Cells[columnName].Value
        

        dt.Rows.Add(newRow);
    
    return dt;

【讨论】:

以上是关于如何使用 sql Bulkcopy 在表中插入 datagridview 行的主要内容,如果未能解决你的问题,请参考以下文章

如何使用sql在表中插入数据类型为BLOB的8796字符长数据?

如何使用 PL/SQL 中的过程在表中插入多个值?

如何在表中插入变量(@id)作为 SQL 服务器查询中的自动增量?

在使用 Entity Framework 数据库优先在表上插入行之前,如何从 PL/SQL 执行触发器?

SQL 在表中插入

PL/SQL-如何使用游标的所有列插入表