I am maintaining a Windows service, recently updated/compiled in .Net 4.5, placed on Server 2012, and using the same SQL Server 2008 database it had been running on before the update. We ran into a snag where it is updating the database using a simple SQL statement, which has apparently always worked before the update. Now the update almost always works, but once in (maybe?) a thousand updates it "fools" us and ExecuteNonQuery returns a 1 indicating it updated the row, but really hasn't.
If you want to tell me what is wrong with the code, please go ahead.. What I'm really hoping for is a suggestion on a more "fool" proof way to handle the update. Maybe it would be better to set the connection to use transactions? Maybe it would be far better to use a stored procedure, as well as perform transactions in the stored procedure? How much better/Why?
The code is pretty simple (too simple?):
try
{
string sql = "UPDATE table SET barcode = '" + newBarcode + "' WHERE pk = '" + reportId + "'"; // no semicolon in the SQL
SqlCommand command = new SqlCommand(sql, connection); // connection recently opened
rowsUpdated = command.ExecuteNonQuery();
if (rowsUpdated != 1)
{
connection.Close()
throw new Exception("...");
}
else
...
connection.Close();
}
catch (Exception ex)
... // general exceptions handled
finally
... // make sure connection is closed
I know it is possible (though very unlikely) that outside influences could be nullifying the field after the update. If that is likely the ONLY possibility, please comment.
答案 0 :(得分:2)
An update that updates the row to the same value is still counted as an affected row.
Compare it with the following:
UPDATE table SET barcode = @x WHERE pk = @id AND barcode <> @x
The explicit guard will skip this row if the update were to "do nothing" and thus it will not be counted as a change if there is no need to make a change.
While one should use placeholders (and using
statements) as applicable, neither of these will change the result of the update statement.
答案 1 :(得分:1)
As suggested in other answer too, use parameterised queries as well use the using
statement to initialize a connection so you don't have to take the responsibility of it closing it when you are done.
Also use and output parameter along with @@ROWCOUNT function to see if any rows has been updated.
Use the RowsAffected
variable to do further processing.
using (SqlConnection conn = new SqlConnection("Connection string here..."))
{
string sqlcmd = "UPDATE table SET barcode = @newBarcode WHERE pk = @reportId SET @RowCount = @@ROWCOUNT;";
conn.Open();
SqlCommand cmd = new SqlCommand(sqlcmd, conn);
cmd.Parameters.Add("@RowCount", SqlDbType.Int).Direction = ParameterDirection.Output;
cmd.Parameters.Add(new SqlParameter("@newBarcode", newBarcode));
cmd.Parameters.Add(new SqlParameter("@reportId", reportId));
rowsUpdated = cmd.ExecuteNonQuery();
int RowsAffected = Convert.ToInt32(cmd.Parameters["@RowCount"].Value);
if (RowsAffected == 0)
{
throw new Exception("...");
}
else
......
}
答案 2 :(得分:-1)
UPDATE查询的ExecuteNonQuery返回值是update语句的where子句匹配的行数,而不是实际更新的行数。
您应该使用连接字符串选项'使用受影响的行'。设置为true时,它将报告已更改的行而不是找到的行。