由于 "string or binary data would be truncated. the statement has been terminated" 错误,执行 SqlCommand 失败

Executing SqlCommand fails due to "string or binary data would be truncated. the statement has been terminated" error

我正在尝试将在 DevExpress 的 GridView 中所做的更改保存到我的数据库的 table 中,并且我能够使用以下代码完成此操作:

using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings[2].ConnectionString))
{
     con.Open();
     GridView gv = sender as GridView;
     using (SqlCommand cmd = con.CreateCommand())
     {                    
          cmd.CommandText = "UPDATE dbo.tb_Alumno set " + e.Column.FieldName + " = '" + e.Value + "' where pk_alumno = " + gv.GetRowCellValue(gv.FocusedRowHandle, gv.Columns[0]);                    
          cmd.ExecuteNonQuery();                    
     }
}               

自从我添加参数命令以防止 SQL 注入以来,我一直遇到这个问题。我已经对每个参数的值进行了硬编码,以遇到提供错误的参数,即 @val 参数:

using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings[2].ConnectionString))            
{
    con.Open();
    GridView gv = sender as GridView;
    using (SqlCommand cmd = con.CreateCommand())
    {                    
         #region Parameters                                        
         //cmd.CommandText = "UPDATE dbo.tb_Alumno set " + e.Column.FieldName + " = '" + e.Value + "' where pk_alumno = " + gv.GetRowCellValue(gv.FocusedRowHandle, gv.Columns[0]);
         cmd.CommandText = "UPDATE dbo.tb_Alumno set @col = @val where pk_alumno = @id";
         cmd.Parameters.AddWithValue("@col", e.Column.FieldName);                    
         cmd.Parameters.AddWithValue("@val", e.Value);
         //cmd.Parameters["@val"].SqlDbType = SqlDbType.VarChar;
         cmd.Parameters.AddWithValue("@id", gv.GetRowCellValue(gv.FocusedRowHandle, gv.Columns[0]));
         #endregion
         try
         { cmd.ExecuteNonQuery(); }
         catch (Exception xe)
         { MessageBox.Show(xe.Message); }                     
    }     
}    

我可以提供的其他信息:

例如,如果我使用以下参数,它会抛出相同的错误:
cmd.Parameters.AddWithValue("@col", "name");
cmd.Parameters.AddWithValue("@val", "More than 6 characters");
cmd.Parameters.AddWithValue("@id", 1);
所有字段都会发生这种情况

如果我把中间那一行改成下面这样我就完全没有问题了
cmd.Parameters.AddWithValue("@val", "This<7");

我已经尝试匹配数据类型,正如您从注释行中看到的那样,但这不会给我带来任何错误并且不会更新 table。我也在其他专栏上测试过这个并且有同样的基本问题。

所以我的问题是,在这种特殊情况下,我应该使用什么代码来防止 SQL inyection?

下面是一个使用动态 sql 的存储过程的示例。注意动态 sql 利用 sp_executesql 这有助于防止 sql 注入攻击。

SQL代码

create PROCEDURE [dbo].[SP_DynamicUpdate]
-- Add the parameters for the stored procedure here
@col varchar(50) = null,
@val varchar(50) = null,
@id int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

-- Insert statements for procedure here
-- declaring strings
DECLARE @SQLString nvarchar(1024);
DECLARE @ParmDefinition nvarchar(512)

    SET @SQLString =
    N'Update 
        dbo.tb_Alumno
    set 
        @col_Param = @val_Param
    where 
        pk_alumno = @id_Param'

-- creating the parameters
SET @ParmDefinition = N'@col_Param varchar(50), ';
SET @ParmDefinition = @ParmDefinition + N'@val_Param varchar(50), ';
SET @ParmDefinition = @ParmDefinition + N'@id_Param int ';

-- Executing the stored procedure
EXECUTE sp_executesql @SQLString, 
                        @ParmDefinition, 
                        @col_Param = @col, 
                        @val_Param = @val, 
                        @id_Param = @id
END

C#代码

using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings[2].ConnectionString))            
{
    GridView gv = sender as GridView;
    using (SqlCommand cmd = new SqlCommand("dbo.SP_DynamicUpdate", con))
    {                    
        #region Parameters                                        
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@col", e.Column.FieldName);                    
        cmd.Parameters.AddWithValue("@val", e.Value);
        cmd.Parameters.AddWithValue("@id", gv.GetRowCellValue(gv.FocusedRowHandle, gv.Columns[0]));
        #endregion
         try
         { 
            con.Open();
            cmd.ExecuteNonQuery(); 
         }
         catch (Exception xe)
         { 
            MessageBox.Show(xe.Message); 
         }                     
     }     
 } 

这将增加更多的安全性,并允许您使用不同的参数分别测试存储过程。您甚至可以在存储过程调用中添加更多检查 - 例如检查是否存在列名。

就您所采用的 sql 注入攻击而言,您无能为力。

我找到了解决问题的最简单方法,如下所示:

我没有注意到,当我在数据网格和数据库之间创建绑定时,会自动创建一个 tableAdapter 和 dataSet。以 this link 作为参考,我发现适配器有一个更新方法,所以我使用了这些信息,所以我简单地使用了这段代码,它就像一个魅力,我还添加了行来验证字段至极,老实说我认为不需要。

Validate();    //Or this.Validate();
tb_AlumnoTableAdapter.Update(escuelaDataSet.tb_Alumno);

和以前一样,我将此代码放在 'CellValueChanged' 事件下,但我假设我可以将它附加到一个按钮以使其成为保存按钮。感谢大家的回答,希望这对像我一样的人有所帮助。

您可能还喜欢阅读:How to: Update Data by Using a TableAdapter