I am using ASP .NET Web Forms . I have 8 Textboxes in which the user can input data for a specific row on the SQL and make changes on the database accordingly for a car, with make, model, year, engine, the rate, availability and location.
I am using the Update Set SQL query as seen on the code line "string query" but it doesn't do anything to the database.
I have established a connection on the SQL Server with Visual Studio 2022
string a = TextBox1.Text;
string b = TextBox2.Text;
string c = TextBox3.Text;
string d = TextBox4.Text;
string ee = TextBox5.Text;
string f = TextBox6.Text;
string g = TextBox7.Text;
string h = TextBox8.Text;
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["TLESConnectionString"].ConnectionString);
conn.Open();
string query = "UPDATE MyCar SET Make = #Make, Model = #Model, Year = #Year ,Engine = #Engine , Rate = #Rate ,Availability = #Availability , Location = #Location";
SqlCommand com = new SqlCommand(query, conn);
com.Parameters.AddWithValue("#Make", a);
com.Parameters.AddWithValue("#Model", b);
com.Parameters.AddWithValue("#Year", c);
com.Parameters.AddWithValue("#Engine", d);
com.Parameters.AddWithValue("#Rate", ee);
com.Parameters.AddWithValue("#Availability", f);
com.Parameters.AddWithValue("#Location", g);
conn.Close();
What is your suggestion as to why it is not updating the Row ?
Your code is missing a few aspects. You are defining the query, which is correct, and you set the parameters and values correctly. You never execute the query, however, and after setting the variables, you close the connection.
I copied your code, made these changes, and it worked for me. Give it a try.
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["TLESConnectionString"].ConnectionString);
conn.Open();
string query = "UPDATE MyCar SET Make = #Make, Model = #Model, Year = #Year ,Engine = #Engine , Rate = #Rate ,Availability = #Availability , Location = #Location";
SqlCommand com = new SqlCommand(query, conn);
com.Parameters.AddWithValue("#Make", a);
com.Parameters.AddWithValue("#Model", b);
com.Parameters.AddWithValue("#Year", c);
com.Parameters.AddWithValue("#Engine", d);
com.Parameters.AddWithValue("#Rate", ee);
com.Parameters.AddWithValue("#Availability", f);
com.Parameters.AddWithValue("#Location", g);
com.ExecuteNonQuery();
As you can see, after inputting all of the variables, I am executing the query, thus making it work. You were just missing:
com.ExecuteNonQuery();
Well done so far.
Dont you need to add a condition as to where you want to update the values?
Example from w3schools
UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition;
Also have you checked if the connection string and the query are both correct?
You can use the Debug for that, check the string value / content for both the connection and the query
On top of that, I noticed that you opened a connection and created a Command, but you didnt run the command, you close the connection, are you running the command?
You could do that with:
try
{
Com.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
EDIT: You only need to use the 'Where' clause if you want to update a specific value rather than the entire table, If you omit the 'WHERE' clause, ALL records will be updated
Related
I am creating a SQL Server database that records vehicle movements, cleans, repairs etc.
I am struggling with how to record the following data.
I need to record each time the vehicle is cleaned. I also need to extract the amount of time that vehicle is cleaned over the past 28 days and make an average
So far I have a master table with the following columns:
Callsign, Registration, Location, Date Last Cleaned.
Then, as a work around I have "vehicle specific" tables which is where I record it each time it is cleaned.
however as I am working the ASP.net, the only way I can find to access the clean dates is by doing a foreach loop through each of the vehicle tables and returning the count of dates in the past 28 days. the problems with this I cannot work out how do this in 1 data connection instead of multiple requests to the server.
Here is a snippet of the code, but you can see that it runs through each of the check box list, and if "true" then will add to the vehicle specific table
string constr = myconstring;
SqlConnection cnn = new SqlConnection(constr);
SqlTransaction transaction;
for (int i = 0; i < checkboxlistAM.Items.Count; i++)
{
string callsignString = checkboxlistAM.Items[i].Text;
if (checkboxlistAM.Items[i].Selected == true)
{
string declare = "declare #morning datetime declare #date datetime declare #counting int ";
string setting = " set #date = DATEADD(d, DATEDIFF(d, 0, getdate()), 0) set #morning = dateadd(hh, 7, #date) set #counting = (select count([made ready]) from["+callsignString+"] where[Made Ready] = #morning) ";
string insertmorning = " if #counting <>1 insert into ["+callsignString+"] ([made ready]) values (#morning) ";
string QueryVehicleSpecificTable = declare + setting + insertmorning;
string QueryMasterTable = "update Shropshire SET[last made Ready AM] = GETDATE() where Callsign = '"+callsignString+"'";
cnn.Open();
transaction = cnn.BeginTransaction();
SqlCommand cmd1 = new SqlCommand(QueryVehicleSpecificTable, cnn);
cmd1.CommandType = CommandType.Text;
SqlCommand cmd2 = new SqlCommand(QueryMasterTable, cnn);
transaction.Commit();
cmd1.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
cnn.Close();
}
else if (checkboxlistAM.Items[i].Selected == false)
{
string declare = "declare #morning datetime declare #date datetime declare #counting int ";
string setting = " set #date = DATEADD(d, DATEDIFF(d, 0, getdate()), 0) set #morning = dateadd(hh, 7, #date) set #counting = (select count([made ready]) from[" + callsignString + "] where[Made Ready] = #morning) ";
string deletemorning = " delete from ["+callsignString+"] where [Made Ready] = #morning";
string queryDeleteRecordfromVehicleSpecific = declare + setting + deletemorning;
string QueryMasterTable = "update Shropshire SET[last made Ready AM] = null where Callsign = '" + callsignString + "'";
cnn.Open();
transaction = cnn.BeginTransaction();
SqlCommand cmd1 = new SqlCommand(QueryMasterTable, cnn);
SqlCommand cmd2 = new SqlCommand(queryDeleteRecordfromVehicleSpecific, cnn);
cmd1.CommandType = CommandType.Text;
cmd2.CommandType = CommandType.Text;
transaction.Commit();
cmd1.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
cnn.Close();
}
}
As you can see this loop has approx. 42 iterations in the morning and afternoon (84 altogether) and opening and closing the connection each time is going to slow everything down.
Does anyone have any ideas of how I can make this better.
Either by making a new "cleaning" table, but then how can I keep track over 28 days instead of forever.
Or by copying each of the "vehicle specific tables" to 1 DataTable and then somehow updating them again after altering them?!?
Any ideas or questions are welcomed.
Thanks in advance
Before I answer your question, I have to point this out:
where Callsign = '"+callsignString+"'"
This is called SQL concatenation, and it allows for SQL injection, which is the #1 security vulnerability in software today. Never do this. Use parametrized SQL queries all the time.
Now to answer your question.
opening and closing the connection each time is going to slow everything down.
That's probably true, but you should obtain proof that it is a performance problem before you decide to optimize it.
Like #PaulF mentions, you don't need to close the connection every time. Move the code that opens and close the connection outside of your loop.
Another technique is to create a stored procedure in your database that would do all this logic in TSQL. That way you can provide only the callsign to the stored proc and it would execute all 84 operations inside a single SQL command. The disadvantage of this approach is that stored procedures are generally a little more expensive to maintain and refactor.
I am somwhat new to SQL, so I am not sure I am going about this the right way.
I am trying to fetch data from my SQL Server database where I want to find out if checkedin is 1/0, but it needs to search on a specific user and sort after the newest date as well.
What I am trying to do is something like this:
string connectionString = ".....";
SqlConnection cnn = new SqlConnection(connectionString);
SqlCommand checkForInOrOut = new SqlCommand("SELECT CHECKEDIN from timereg ORDER BY TIME DESC LIMIT 1 WHERE UNILOGIN = '" + publiclasses.unilogin + "'", cnn);
So my question, am I doing this right? And how do I fetch the data collected, if everything was handled correctly it should return 1 or 0. Should I use some sort of SqlDataReader? I am doing this in C#/WPF
Thanks
using (SqlDataReader myReader = checkForInOrOut.ExecuteReader())
{
while (myReader.Read())
{
string value = myReader["COLUMN NAME"].ToString();
}
}
This is how you would read data from SQL, but i recommend you looking into Parameters.AddWithValue
There are some errors in your query. First WHERE goes before ORDER BY and LIMIT is an MySql keyword while you are using the Sql Server classes. So you should use TOP value instead.
int checkedIn = 0;
string cmdText = #"SELECT TOP 1 CHECKEDIN from timereg
WHERE UNILOGIN = #unilogin
ORDER BY TIME DESC";
string connectionString = ".....";
using(SqlConnection cnn = new SqlConnection(connectionString))
using(SqlCommand checkForInOrOut = new SqlCommand(cmdText, cnn))
{
cnn.Open();
checkForInOrOut.Parameters.Add("#unilogin", SqlDbType.NVarChar).Value = publiclasses.unilogin;
// You return just one row and one column,
// so the best method to use is ExecuteScalar
object result = checkForInOrOut.ExecuteScalar();
// ExecuteScalar returns null if there is no match for your where condition
if(result != null)
{
MessageBox.Show("Login OK");
// Now convert the result variable to the exact datatype
// expected for checkedin, here I suppose you want an integer
checkedIN = Convert.ToInt32(result);
.....
}
else
MessageBox.Show("Login Failed");
}
Note how I have replaced your string concatenation with a proper use of parameters to avoid parsing problems and sql injection hacks. Finally every disposable object (connection in particular) should go inside a using block
I have a slight issue, I have a ASP.NET Webforms application. I'm sending over a url?id=X were X is my database index or id.
I have a C# class file to run my SQL connection and query. Here is the code:
public DataTable ViewProduct(string id)
{
try
{
string cmdStr = "SELECT * Products WHERE Idx_ProductId = " + id;
DBOps dbops = new DBOps();
DataTable vpTbl = dbops.RetrieveTable(cmdStr, ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString);
return vpTbl;
}
catch (Exception e)
{
return null;
}
}
So as you can see my problem lies within string cmdStr = "SQL Query" + variable;
I'm passing over my index or id through the URL then requesting it and turning it into a string then using ViewProduct(productId).
I don't know what syntax or how to add the id into my C# string sql query. I've tried:
string cmdStr = "SELECT * Products WHERE Idx_ProductId = #0" + id;
string cmdStr = "SELECT * Products WHERE Idx_ProductId = {0}" + id;
also what I have currently to no avail.
I was so sure this would be a duplicate of some canonical question about parameterized queries in C#, but apparently there isn't one (see this)!
You should parameterize your query - if you don't, you run the risk of a malicious piece of code injecting itself into your query. For example, if your current code could run against the database, it would be trivial to make that code do something like this:
// string id = "1 OR 1=1"
"SELECT * Products WHERE Idx_ProductId = 1 OR 1=1" // will return all product rows
// string id = "NULL; SELECT * FROM UserPasswords" - return contents of another table
// string id = "NULL; DROP TABLE Products" - uh oh
// etc....
ADO.NET provides very simple functionality to parameterize your queries, and your DBOps class most assuredly is not using it (you're passing in a built up command string). Instead you should do something like this:
public DataTable ViewProduct(string id)
{
try
{
string connStr = ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (SqlCommand cmd = conn.CreateCommand())
{
// #id is very important here!
// this should really be refactored - SELECT * is a bad idea
// someone might add or remove a column you expect, or change the order of columns at some point
cmd.CommandText = "SELECT * Products WHERE Idx_ProductId = #id";
// this will properly escape/prevent malicious versions of id
// use the correct type - if it's int, SqlDbType.Int, etc.
cmd.Parameters.Add("#id", SqlDbType.Varchar).Value = id;
using (SqlDataReader reader = cmd.ExecuteReader())
{
DataTable vpTbl = new DataTable();
vpTbl.Load(reader);
return vpTbl;
}
}
}
}
catch (Exception e)
{
// do some meaningful logging, possibly "throw;" exception - don't just return null!
// callers won't know why null got returned - because there are no rows? because the connection couldn't be made to the database? because of something else?
}
}
Now, if someone tries to pass "NULL; SELECT * FROM SensitiveData", it will be properly parameterized. ADO.NET/Sql Server will convert this to:
DECLARE #id VARCHAR(100) = 'NULL; SELECT * FROM SensitiveData';
SELECT * FROM PRoducts WHERE Idx_ProductId = #id;
which will return no results (unless you have a Idx_ProductId that actually is that string) instead of returning the results of the second SELECT.
Some additional reading:
https://security.stackexchange.com/questions/25684/how-can-i-explain-sql-injection-without-technical-jargon
Difference between Parameters.Add and Parameters.AddWithValue
SQL injection on INSERT
Avoiding SQL injection without parameters
How do I create a parameterized SQL query? Why Should I? (VB.NET)
How can I prevent SQL injection in PHP? (PHP specific, but many helpful points)
Is there a canonical question telling people why they should use SQL parameters?
What type Products.Idx_ProductId is?
Probably it is string, than you need to use quotes: "... = '" + id.Trim() + "'";
I'm running a query from a web form to update records. Since I'm just learning about C#, I'm using a command string as opposed to a stored procedure.
My update method is as follows:
public void updateOne()
{
string commandText = "update INVOICE SET <Redacted> = #<Redacted>,
Supplier = #Sup, SupplierName = #SupN, NetTotal = #Net,
VATTotal = #VAT, InvoiceDate = #InvDt "
<needed a line break here, which is why I split the string again>
+ "WHERE K_INVOICE = #K_INV";
using (SqlConnection dbConnection = new SqlConnection
(conParams.connectionString))
{
SqlCommand cmd = new SqlCommand(commandText, dbConnection);
cmd.Parameters.Add("#K_INV", SqlDbType.Int);
cmd.Parameters["#K_INV"].Value = #K_INV;
cmd.Parameters.AddWithValue("#<Redacted>", #<Redacted>.ToString());
cmd.Parameters.AddWithValue("#Sup", #Sup.ToString());
cmd.Parameters.AddWithValue("#SupN", #SupN.ToString());
cmd.Parameters.AddWithValue("#Net", #Net.ToString());
cmd.Parameters.AddWithValue("VAT", #VAT.ToString());
cmd.Parameters.AddWithValue("#InvDt", #InvDt.ToString());
try
{
dbConnection.Open();
cmd.ExecuteNonQuery();
}
catch (Exception e)
{
errorString = e.Message.ToString();
}
}
}
Catch stalls on an SQL error (Incorrect syntax near SET), and I have an idea that the issue occurs because I convert the parameters to strings. The first parameter is an Int, which should be OK.
If this is the case, what should I convert the parameters to? If not, what on earth is wrong?
Try to add a # before the string to escape the breaklines, for sample:
string commandText = #"update INVOICE SET [Redacted] = #Redacted,
Supplier = #Sup, SupplierName = #SupN, NetTotal = #Net,
VATTotal = #VAT, InvoiceDate = #InvDt "
+ "WHERE K_INVOICE = #K_INV";
In parameterName argument you can add the # but the value not, just the variable, for sample
cmd.Parameters.AddWithValue("#Redacted", redacted.ToString());
Try to execute this query in the databse with some values to check if everything is correct. You could use [brackets] in the table name and column names if you have a reserved word.
I would recommend you read this blog article on the dangers of .AddWithValue():
Can we stop using AddWithValue already?
Instead of
cmd.Parameters.AddWithValue("#Sup", #Sup.ToString());
you should use
cmd.Parameters.Add("#Sup", SqlDbType.VarChar, 50).Value = ...(provide value here)..;
(is your variable in C# really called #SupN ?? Rather unusual and confusing....)
I would recommend to always define an explicit length for any string parameters you define
Here's the issue: no matter what I seem to do, I can't get a MySqlCommand to actually prepare. I've tried copy/pasting the example code from http://dev.mysql.com/doc/refman/5.6/en/connector-net-programming-prepared.html with very slight modifications, but that does not actually work either.
I scoured Google to try and find a solution, but the closest thing that came up was: MySql statement prepare "not sticking" which did not actually answer the question.
Here's my table setup for this test:
CREATE TABLE `test`.`test_prepared_query` (
`id` INT NOT NULL ,
`value` INT NOT NULL ,
PRIMARY KEY (`id`) );
Test code in C#
public void TestPrepareQuery()
{
connString = new MySqlConnectionStringBuilder();
connString.Server = "localhost";
connString.Database = "test";
connString.UserID = "someuserid";
connString.Password = "somepassword";
bool isprepared;
using (MySqlConnection conn = new MySqlConnection(connString.ToString()))
{
MySqlCommand cmd = new MySqlCommand(
"INSERT INTO test_prepared_query VALUES (#id, #value)", conn);
cmd.Connection.Open();
cmd.Prepare();
isprepared = cmd.IsPrepared; // isprepared is false here
cmd.Parameters.AddWithValue("#id", 0);
cmd.Parameters.AddWithValue("#value", 0);
cmd.Prepare();
isprepared = cmd.IsPrepared; // isprepared is still false
// this is 1 -- the query succeeds
int rowsAffected = cmd.ExecuteNonQuery();
for (int i = 1; i < 10; i++)
{
cmd.Parameters["#id"].Value = i;
cmd.Parameters["#value"].Value = i;
// this is 1 -- the query succeeds
rowsAffected = cmd.ExecuteNonQuery();
}
}
}
When I run the code, it does successfully put rows in the table, but stepping through the program reviews that the state of cmd.IsPrepared is always false. Does anyone know why this could be happening? This source code is essentially identical to the example code, with only modifications to the table name as well as real connection strings.
Edit:
I've tried variables with ?name format, and that does not work either. I've also tried only having one cmd.Prepare() method call at a time in the tests.
I eventually pulled up the source code for MySql Connector / .NET and discovered that if MySqlCommand.Connection.Settings.IgnorePrepare = true (which is the default!), then calling Prepare is a no op.
The way to fix this is to explicitly set IgnorePrepare to false in the connection string. This can be done rather easily with a MySqlConnectionStringBuilder using the following code snippet:
MySqlConnectionStringBuilder connBuilder = new MySqlConnectionStringBuilder();
// .. set up the rest of your connection
connBuilder.IgnorePrepare = false;
MySqlConnection conn = new MySqlConnection(connBuilder.ToString());