首页 > 解决方案 > DataTable update() 插入重复的新行而不检查它是否存在

问题描述

我正在尝试使用 update() 方法,但它正在将我的数据表数据插入到我的数据库中,而不检查该行是否存在,因此它正在插入重复数据。它也不会删除数据表中不存在的行。如何解决这个问题?我想将我的数据表与服务器表同步。

private void Form1_Load(object sender, EventArgs e)
{

    // TODO: This line of code loads data into the 'MyDatabaseDataSet11.Vendor_GUI_Test_Data' table. You can move, or remove it, as needed.
    this.vendor_GUI_Test_DataTableAdapter.Fill(this.MyDatabaseDataSet11.Vendor_GUI_Test_Data);

    // read target table on SQL Server and store in a tabledata var
    this.ServerDataTable = this.MyDatabaseDataSet11.Vendor_GUI_Test_Data;
}

插入

private void convertGUIToTableFormat()
{
    ServerDataTable.Rows.Clear();

    // loop through GUIDataTable rows
    for (int i = 0; i < GUIDataTable.Rows.Count; i++)
    {
        String guiKEY = (String)GUIDataTable.Rows[i][0] + "," + (String)GUIDataTable.Rows[i][8] + "," + (String)GUIDataTable.Rows[i][9];
        //Console.WriteLine("guiKey: " + guiKEY);

        // loop through every DOW value, make a new row for every true
        for(int d = 1; d < 8; d++)
        {
            if ((bool)GUIDataTable.Rows[i][d] == true)
            {
                DataRow toInsert = ServerDataTable.NewRow();
                toInsert[0] = GUIDataTable.Rows[i][0];
                toInsert[1] = d + "";
                toInsert[2] = GUIDataTable.Rows[i][8];
                toInsert[3] = GUIDataTable.Rows[i][9];

                ServerDataTable.Rows.InsertAt(toInsert, 0);
                //printDataRow(toInsert);
                //Console.WriteLine("---------------");
            }
        }
    }

正在尝试更新

// I got this adapter from datagridview, casting my datatable to their format
CSharpFirstGUIWinForms.MyDatabaseDataSet1.Vendor_GUI_Test_DataDataTable DT = (CSharpFirstGUIWinForms.MyDatabaseDataSet1.Vendor_GUI_Test_DataDataTable)ServerDataTable;

DT.PrimaryKey = new DataColumn[] { DT.Columns["Vendor"], DT.Columns["DOW"], DT.Columns["LeadTime"], DT.Columns["DemandPeriod"] };

this.vendor_GUI_Test_DataTableAdapter.Update(DT);

标签: c#sql-serverdatatabledatagridview

解决方案


让我们看看发布的代码中发生了什么。
首先这一行:

 this.ServerDataTable = this.MyDatabaseDataSet11.Vendor_GUI_Test_Data;

这不是副本,而只是两个变量之间的赋值。分配的一个(ServerDataTable)接收对存储来自数据库的数据的内存区域的“引用”。所以这两个变量“指向”同一个内存区域。无论你对一个人做什么都会影响另一个人看到的东西。

现在看看这一行:

ServerDataTable.Rows.Clear();

呃!为什么?您正在清除从数据库加载的数据所在的内存区域。现在 Datatable 是空的,并且那里没有记录 (DataRow)。
让我们看看循环内部发生了什么

DataRow toInsert = ServerDataTable.NewRow();

创建了一个新的 DataRow,现在每个 DataRow 都有一个名为RowState的属性,当您创建新行时,此属性的默认值为DataRowState.Detached,但是当您在 DataRow 集合中添加行时

ServerDataTable.Rows.InsertAt(toInsert, 0);

然后DataRow.RowState属性变为DataRowState.Added

此时缺少的信息是当您调用 TableAdapter 时的行为方式Update。适配器需要构建适当的 INSERT/UPDATE/DELETE sql 命令来更新数据库。用于选择正确 sql 命令的信息是什么?实际上,它会查看该RowState属性,并发现您的所有行都处于“已添加”状态。因此,它为您的表选择 INSERT 命令,并禁止任何重复的键违规,您将在表中以重复记录结束。

你应该怎么做才能解决问题?那么第一件事是从加载的数据中删除清除内存的行,然后,而不是总是调用,InsertAt您应该首先查看内存中是否已经有该行。您可以使用DataTable.Select方法执行此操作。此方法需要一个字符串,就像它是 WHERE 语句一样,您应该为表的主键使用一些值

var rows =  ServerDataTable.Select("PrimaryKeyFieldName = " + valueToSearchFor);

如果您得到的行数大于零,那么您可以使用返回的第一行并使用您的更改更新现有值,如果没有符合条件的行,那么您可以像现在一样使用 InsertAt。


推荐阅读