sqladaptor插入到不同的表中

时间:2016-04-01 22:38:22

标签: c# datagridview sqldataadapter

我想创建一个可以同时更新旧表并记录更改前的数据的函数。 (这就像一个日志表。)我创建了一个表来检索我搜索的表,另一个表来存储更改前的表。这两个表在列上有一些差异。 DS是检索数据集,ADS是仅执行插入

的日志数据集
private void Update_Button_Click(object sender, EventArgs e)
    {
        try
        {
            //Update table1
            SqlCmb = new SqlCommandBuilder(SqlAd);
            SqlAd.Update(DS, "dataset1");

                //INSERT table1_log
                DataSet ds = new DataSet();
                ds = ADS;
                ds.Tables[0].Columns.Add("USER_NAME", typeof(string));
                ds.Tables[0].Columns.Add("UNIQ", typeof(String));
            foreach (DataRow dr in ds.Tables[0].Rows)
                {
                    dr["USER_NAME"] = Form1.USER_NAME.Text;
                    dr["ALTER_TIME"] = DateTime.Now;
                }
                SqlDataAdapter SqlAdI = new SqlDataAdapter("SELECT * FROM table1_log where 0 = 1", SqlCon);
                SqlCommandBuilder SqlCmbI = new SqlCommandBuilder(SqlAdI);

                SqlCom= new SqlCommand("INSERT INTO table1_log FROM" + ds.Tables[0], SqlCon) ;
                SqlAdI.InsertCommand = SqlCom;
                SqlAdI.Update(ADS, "table1");


            MessageBox.Show("Info updated", "Update", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        catch(Exception ex)
        {
            MessageBox.Show(ex.Message,"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

我的问题是它无法插入日志表,更新到table1工作正常。 Uniq是PK并由sql server自动生成。错误消息是“并发冲突:更新命令影响了预期的1条记录中的0条。”

谢谢!

1 个答案:

答案 0 :(得分:0)

如评论中所述,您应该创建一个触发器。触发器不会使您的代码复杂化或使您远离使用DataGridView,它实际上会使您的代码更简单(IMO,您的代码有几个问题,但我会尝试解决这个问题)。触发器在数据库级别实现,而不是在C#代码中实现。它将消除在代码中显式编写insert语句的需要,如果您编写需要记录的多个语句(例如插入,更新,删除),这将特别有用。您只需要一次编写插入到日志语句,它将始终运行。

我想知道你插入的问题是因为你的日志表的主键与table1的主键匹配。在这种情况下,可以多次更新单个记录并尝试使用相同的密钥,该密钥已经存在于日志中。最简单的解决方法是在日志表中添加一个新的主键,它只是一个在每个插入上递增的顺序id。我建议添加另一列以指示执行的操作类型(即插入,更新,删除)。这是一个很好的答案,解释了如何检测操作:Trigger in SQL Server - Get the type of Transaction done for Audit Table

更重要的一点是,您正在通过连接字符串来构建插入语句。我不完全确定ds.Tables [0]的值来自哪里,但是你将它直接连接到一个insert语句,如果用户输入那么你就会面临SQL注入攻击的风险。攻击者可以非常轻松地窃取或删除数据。即使它在其他地方被检查过,我仍然建议使用SqlParameter,这样任何看到你的代码的人都可以清楚地看到你正在防范这些危险。

用于在sql中创建触发器的伪代码:

CREATE TRIGGER TR_TABLE1_AUDIT
ON table1
FOR INSERT, UPDATE, DELETE
AS
  -- your insert statement here
  INSERT INTO table1_log ...

编辑:添加有关防止SQL注入攻击的信息。 您允许用户输入的字段数无关紧要,这与SQL注入攻击根本无关。为了防止它,您不能将用户输入直接连接到SQL语句。你需要做这样的事情:

// Set the command text (you could pass this in the constructor)
sqlcom.CommandText = "select * from table1 where loc = @loc ";
// Create SqlParameter
// Note that I don't know your actual SqlDbType, replace it with whatever it should be
SqlParameter param = new SqlParameter("@loc", SqlDbType.Varchar) { Value = loc };
// Add to SqlCommand parameter collection
sqlcom.Parameters.Add(param);
// Add the command to your SqlDataAdapter if you are using one

这称为参数化。参数化用户输入的任何内容将保护您免受SQL注入。您可以使用Add语句内联SqlParameter来缩短范围。