这个简单的更新查询有什么问题?

时间:2010-05-09 20:52:24

标签: c# asp.net sql sql-server

除了不安全...我没有收到任何错误消息,但行没有更新。行整数在查询后设置为1,表示1行受到影响。

String query = "UPDATE contacts SET contact_name = '" + ContactName.Text.Trim() + "', " +
            "contact_phone = '" + Phone.Text.Trim() + "', " +
            "contact_fax = '" + Fax.Text.Trim() + "', " +
            "contact_direct = '" + Direct.Text.Trim() + "', " +
            "company_id = '" + Company.SelectedValue + "', " +
            "contact_address1 = '" + Address1.Text.Trim() + "', " +
            "contact_address2 = '" + Address2.Text.Trim() + "', " +
            "contact_city = '" + City.Text.Trim() + "', " +
            "contact_state = '" + State.SelectedValue + "', " +
            "contact_zip = '" + Zip.Text.Trim() + "' " + 
            "WHERE contact_id = '" + contact_id + "'";

String cs = Lib.GetConnectionString(null);
SqlConnection conn = new SqlConnection(cs);

SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = query;
cmd.Connection.Open();
int rows = cmd.ExecuteNonQuery();

6 个答案:

答案 0 :(得分:9)

您应该使用参数化查询有以下几个原因:

  • 消除了嵌入式违反查询的可能性
  • 保护您的数据库/网站免受SQL注入

此外,如果cmd返回1,则更新行。您可能需要检查您的期望......

String query = @"
    UPDATE contacts 
    SET contact_name = @contact_name, contact_phone = @contact_phone, contact_fax = @contact_fax, 
        contact_direct = @contact_direct , company_id = @company_id, contact_address1 = @contact_address1, 
        contact_address2 =@contact_address2, contact_city = @contact_city , contact_state = @contact_state,  
        contact_zip = @contact_zip 
    WHERE contact_id = @contact_id";

String cs = Lib.GetConnectionString(null);
using (SqlConnection conn = new SqlConnection(cs))
{
    using (SqlCommand cmd = conn.CreateCommand())
    {
        cmd.Parameters.AddWithValue("@contact_name", ContactName.Text.Trim());
        cmd.Parameters.AddWithValue("@contact_phone", Phone.Text.Trim());
        cmd.Parameters.AddWithValue("@contact_fax", Fax.Text.Trim());
        cmd.Parameters.AddWithValue("@contact_direct", Direct.Text.Trim());
        cmd.Parameters.AddWithValue("@company_id", Company.SelectedValue);
        cmd.Parameters.AddWithValue("@contact_address1", Address1.Text.Trim());
        cmd.Parameters.AddWithValue("@contact_address2", Address2.Text.Trim());
        cmd.Parameters.AddWithValue("@contact_city", City.Text.Trim());
        cmd.Parameters.AddWithValue("@contact_state", State.SelectedValue);
        cmd.Parameters.AddWithValue("@contact_zip", Zip.Text.Trim());
        cmd.Parameters.AddWithValue("@contact_id", contact_id);
        cmd.CommandText = query;
        cmd.Connection.Open();
        int rows = cmd.ExecuteNonQuery();
    }
}

现在,这看起来不那么干净吗?当你明白为什么时,它会给你一种温暖的模糊感觉,让你在晚上睡个好觉。 ; - )

<强>澄清

您说明的问题是“此查询有什么问题”。答案是什么。查询没有错。

问题在于您的网页代码,即使有5人建议查询没有任何问题,您也没有发帖。

我的意思是'您可能需要检查您的期望......'并且'您需要检查执行查询之前进入参数的数据以确保它们是您想要的',John更清楚地描述了但让我简要地再说一遍:

如图所示的查询表面上是正确的,应该按预期执行。您可能遇到的是周围代码逻辑的缺陷。

很可能你在page_load中进行数据绑定而没有IsPostback保护,因此用原始数据覆盖你的输入值。

只有在第一次加载时才需要加载和绑定控件/页面。此后,控件的状态将保存在viewstate中,该视图状态存储在html的隐藏字段中。如果您只是在每个页面加载时绑定到数据源,您将覆盖任何输入

让我们来看看它是如何工作的。

这是一个合适的逻辑流程

protected void Page_Load(object sender, EventArgs e)
{
    System.Diagnostics.Trace.WriteLine("Page_Load");
    if (!IsPostBack)
    {
        System.Diagnostics.Trace.WriteLine("\tBind TextBox1");
        TextBox1.Text = "Initial Value";
    }
    System.Diagnostics.Trace.WriteLine("\tTextBox1.Text = " + TextBox1.Text);
}

protected void Button1_Click(object sender, EventArgs e)
{
    System.Diagnostics.Trace.WriteLine("Button1_Click");
    System.Diagnostics.Trace.WriteLine("\tTextBox1.Text = " + TextBox1.Text);
}

如果您加载此页面,请在文本框中输入“新值”,然后单击按钮1,这是Trace显示的内容:

    Page_Load
        Bind TextBox1
        TextBox1.Text = Initial Value

    Page_Load
        TextBox1.Text = New Value

    Button1_Click
        TextBox1.Text = New Value

但没有后卫:

protected void Page_Load(object sender, EventArgs e)
{
    System.Diagnostics.Trace.WriteLine("Page_Load");
    System.Diagnostics.Trace.WriteLine("\tBind TextBox1");
    TextBox1.Text = "Initial Value";
    System.Diagnostics.Trace.WriteLine("\tTextBox1.Text = " + TextBox1.Text);
}

        protected void Button1_Click(object sender, EventArgs e)
{
    System.Diagnostics.Trace.WriteLine("Button1_Click");
    System.Diagnostics.Trace.WriteLine("\tTextBox1.Text = " + TextBox1.Text);
}

您会得到您可能遇到的结果......

    Page_Load
        Bind TextBox1
        TextBox1.Text = Initial Value

    Page_Load
        Bind TextBox1
        TextBox1.Text = Initial Value

    Button1_Click
        TextBox1.Text = Initial Value

但是,一个实用的调试技术可以帮助您在执行查询之前中断更新方法并检查值以验证它们是您认为的那样然后从那里开始。

答案 1 :(得分:3)

如果它仍然无效,这里有一些调试问题:

  • 此代码位于何处以及页面生命周期的哪个部分被调用?
  • 什么是Lib.GetConnectionString(null)返回?
  • 您是否在更新查询之前设置了任何断点以确保存在正确的数据?

我最好的猜测是,由于您正在更新现有记录,因此数据库中的现有数据很可能会在Page_Load上覆盖您的回发值,这意味着查询实际上正在更新,但是它使用与其中已有的完全相同的数据进行更新。

您要做的是在填充页面上的任何文本框之前检查if(!IsPostBack)...。这样,如果有回发,则文本框中的数据将填充回发值而不是数据库中的值。

答案 2 :(得分:2)

您可以尝试进行快速故障排除的另一件事是使用SQL Server探查器。它比你想象的更容易使用。我保证!

  1. 运行Sql Server Profiler(随Sql server一起提供)
  2. 点击文件 - &gt;新踪迹
  3. 连接到您的数据库
  4. 接受默认跟踪属性。
  5. 轰!这是录音!
  6. 运行您的应用。如果您已经在进行更新的页面上,请对其进行刷新,或点击按钮或导致更新的任何内容。
  7. 检查跟踪中显示的记录。这都是实时的!
  8. 可选:单击“删除”按钮,然后重试。您将能够看到在探查器中执行的确切SQL。
  9. 完成后,点击停止按钮。

答案 3 :(得分:1)

首先,它极易受到注射攻击。

在更多的日常级别,包含单个撇号字符的任何输入都将破坏语法。您的任何数据是否恰好都包含撇号?

编辑 - 第二个想法 正如您刚刚学习的那样,请注意,一旦完成,必须调用conn.Close();关闭与数据库的连接,否则连接将在数据库服务器上保持打开状态,直到垃圾收集完成处理你的conn个实例。或者,请参阅using () {}构造。

稍微抓住稻草,但尝试明确的交易。

SqlTransaction trans;
conn.Open(); 
try 
{
    trans = conn.BeginTransaction();
    int rows = cmd.ExecuteNonQuery(); 
    trans.Commit();
}
catch 
{
     trans.Rollback();
}
conn.Close();

答案 4 :(得分:1)

查看此问题中的其他讨论,看起来您的查询有效,但这里不相关的内容是错误的。有些事情要检查,有时咬我的屁股:

  • 确实确定您正在运行您认为自己的查询吗?在点击cmd.ExecuteNonQuery()之前进入调试器。参数是你认为的吗?命令文本是否仍然是您认为的应该是什么?

  • 您是否正在更新正确的数据库?进入调试器并检查连接字符串(cs)。当您需要开发数据库时,或许您仍然连接到生产数据库,反之亦然?

  • 如果在SQL Management Studio中手动运行查询,会发生什么?该行是否更新为您认为应该的内容?

答案 5 :(得分:0)

如果contact_id是整数,请不要将其放在单引号中。

变化:

"WHERE contact_id = '" + contact_id + "'";

"WHERE contact_id = " + contact_id;

更新:正如约翰在评论中指出的那样,这两个条款没有显着差异。在没有引号的情况下查询可能会更快,因为在这种情况下您正在跳过强制转换/转换。