private void Update_Record_Click(object sender, EventArgs e)
{
ConnectionClass.OpenConnection();
if (textBox4.Text == "" && textBox2.Text == "")
{
MessageBox.Show("No value entred for update.");
}
else if (textBox4.Text != "" && textBox2.Text != "")
{
SqlCommand cmd = new SqlCommand("update medicinerecord set quantity='" + textBox2.Text + "' where productid='"+comboBox1.Text+"'", ConnectionClass.OpenConnection());
cmd.ExecuteNonQuery();
cmd = new SqlCommand("update myrecord set price='" + textBox4.Text + "' where productid='" + comboBox1.Text + "'", ConnectionClass.OpenConnection());
cmd.ExecuteNonQuery();
ConnectionClass.CloseConnection();
}
else if (textBox2.Text != "")
{
SqlCommand cmd = new SqlCommand("update myrecord set quantity='" + textBox2.Text + "' where productid='" + comboBox1.Text + "'", ConnectionClass.OpenConnection());
cmd.ExecuteNonQuery();
ConnectionClass.CloseConnection();
}
else if (textBox4.Text != "")
{
SqlCommand cmd = new SqlCommand("update myrecord set price='" + textBox4.Text + "' where productid='" + comboBox1.Text + "'", ConnectionClass.OpenConnection());
cmd.ExecuteNonQuery();
ConnectionClass.CloseConnection();
}
}
它工作正常但我想缩短它以便更容易理解。我怎么能重构它?
答案 0 :(得分:6)
免责声明:正如Darin所说,我稍微改变了原来的解决方案。布朗博士。
此代码 large 的事实是最不重要的问题。这里有一个更大的SQL注入问题。您应该使用参数化查询来避免这种情况。
所以我首先将数据访问逻辑外部化为一个单独的方法:
public void UpdateMedicineRecordQuantity(string tableName, string attributeName, string productId, string attributeValue)
{
using (var conn = new SqlConnection("YOUR ConnectionString HERE"))
using (var cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText = "UPDATE " + tableName + "SET " + attributeName+ " = @attributeValue where productid = @productid";
cmd.Parameters.AddWithValue("@attributeValue", attributeValue);
cmd.Parameters.AddWithValue("@productid", productId);
cmd.ExecuteNonQuery();
}
}
然后:
string productId = comboBox1.Text;
string quantity = textBox2.Text;
UpdateMedicineRecordQuantity("medicinerecord", "quantity", productId, quantity);
使用“tableName”和“attributeName”作为SQL的动态部分是没有安全问题的,只要您不让用户为这两个参数提供输入。
您可以继续为其他情况重复使用此方法。
答案 1 :(得分:3)
像if (textBox4.Text == "" && textBox2.Text == "")
这样的语句根本没有任何意义,如果你不能快速了解应用程序的特定部分是什么。在这种情况下,似乎它们各自代表一个值,并且它们中的至少一个应包含某些操作合法的东西。研究SQL语句表明textBox2
是一个数量而textBox4
是一个价格。首先,将这些控件名称更改为更有意义的名称。
其次,我将检查包装到具有更具描述性名称的方法中:
private bool HasPriceValue()
{
return !string.IsNullOrEmpty(textBox4.Text);
}
private bool HasQuantityValue()
{
return !string.IsNullOrEmpty(textBox2.Text);
}
然后你可以重写if-block:
if (!HasPriceValue() && !HasQuantityValue())
{
MessageBox.Show("No value entred for update.");
}
else
{
ConnectionClass.OpenConnection();
if (HasQuantityValue())
{
SqlCommand cmd = new SqlCommand("update medicinerecord set quantity='" + textBox2.Text + "' where productid='"+comboBox1.Text+"'", ConnectionClass.OpenConnection());
cmd.ExecuteNonQuery();
}
if (HasPriceValue())
{
SqlCommand cmd = new SqlCommand("update myrecord set price='" + textBox4.Text + "' where productid='" + comboBox1.Text + "'", ConnectionClass.OpenConnection());
cmd.ExecuteNonQuery();
}
ConnectionClass.CloseConnection();
}
这样您就不会在代码中重复SQL查询,并且很容易阅读代码并理解它的作用。下一步是重写SQL查询以使用参数而不是连接字符串(这会打开SQL注入攻击的代码),as suggested by Darin。
答案 2 :(得分:1)
为每个命令(在if
语句中)创建字符串(或字符串数组),如果字符串不为null,则之后运行sql。
[注]
您不会在任何情况下关闭连接
[/注]
答案 3 :(得分:1)
除了让它更容易理解之外,你可能想要重构它以便更容易测试。
在这种情况下,可能需要摆脱ConnectionClass
单例(而是使用IoC并注入连接工厂)和MessageBox(例如抛出异常并让周围的代码确定如何显示此消息)
答案 4 :(得分:1)
使代码“小”可能不是使其可读或“易于理解”的最佳方式。我发现更多简洁的代码要比使用高质量变量,方法,属性,类名等精心布置的代码更难理解。
我认为更严重的问题不是代码的大小,而是您易受SQL注入攻击的事实。
这是我写回来的一篇文章,你可能觉得它很有用:SQL Injection attacks and some tips on how to prevent them
我希望有所帮助。
答案 5 :(得分:1)
您的代码是等待发生的SQL注入。注意'ol Johnny Tables。
答案 6 :(得分:1)
我对您的代码进行了简化和优化,并提供了一些评论。
private void Update_Record_Click(object sender, EventArgs e)
{
if (textBox4.Text == "" && textBox2.Text == "")
{
MessageBox.Show("No value entered for update.");
return;
}
ConnectionClass.OpenConnection();
var cmd = new SqlCommand("update medicinerecord set quantity='" + textBox2.Text + "' where productid='" + comboBox1.Text + "'", ConnectionClass.OpenConnection());
cmd.ExecuteNonQuery();
cmd = null;
if (textBox2.Text != "")
cmd = new SqlCommand("update myrecord set quantity='" + textBox2.Text + "' where productid='" + comboBox1.Text + "'", ConnectionClass.OpenConnection());
else if (textBox4.Text != "")
cmd = new SqlCommand("update myrecord set price='" + textBox4.Text + "' where productid='" + comboBox1.Text + "'", ConnectionClass.OpenConnection());
if (cmd != null) cmd.ExecuteNonQuery();
ConnectionClass.CloseConnection();
}
if
)上的大括号来立即简化代码。我已经为你做了这件事。update myrecord set price='" + textbox4.Text + "'
..应该至少update myrecord set price='" + textbox4.Text.Replace("'", "''")
..以避免可能的SQL injection攻击。.Text == ""
,通常首选在.NET 4上使用!string.IsNullOrEmpty()
或!string.IsNullOrWhitespace()
来覆盖空字符串和空字符串。var
。OpenConnection()
和CloseConnection()
来简化此代码验证发生后,此方法。还修复了拼写错误! :)
答案 7 :(得分:0)
您可以使用当您不想更新字段时可以设置为NULL
的参数:
UPDATE myrecord SET price = ISNULL(@price, price) -- MSSQL
然后,您可以使用Command.Parameters.AddWithValue
方法根据文本字段使用一个命令。对DBNull.Value
使用NULL
。即使您不更改结构,也应该这样做以防止SQL注入攻击。
干杯,马蒂亚斯
答案 8 :(得分:0)
你可以看到
cmd.ExecuteNonQuery();
ConnectionClass.CloseConnection();
是多余的,所以为什么不在最后一个else if
范围之后移动它。
答案 9 :(得分:0)
为什么不简单地再创建一个方法:
void ExecuteCommand(string sql)
{
SqlCommand cmd = new SqlCommand(sql);
cmd.ExecuteNonQuery();
ConnectionClass.CloseConnection();
}