Foreach loop with if statement ERROR - c#

So I figured I would paste the whole thing, but the foreach loop section isn't working. This is for an add page, and when I publish it and try to search, this is the error page: https://i.imgur.com/9WYBE4G.png. Also, this is what the add page looks like: http://i.imgur.com/8QkFLzW.png.
Referring to the error page, it says "incorrect syntax near "("". Does anyone know how to fix this?
I did originally not have a space before "VALUES" in the query, so that would've been an issue but it isn't anymore
I believe that the issue lies within the logic, or something very small with the formatting that I'm not aware of
I also did not copy and paste this code - it was given to me like this, and my task is to find out how it works and add comments. I'm also supposed to fix this error, which lies in the foreach loop I believe. I don't know c# well enough to know what the error could be
Added comma before GETDATE()
public void ProcessRequest(HttpContext context)
{
// create new Sql connection
SqlConnection connection = new SqlConnection(Properties.Settings.Default.ConnectionString);
connection.Open();
// insert into query
string query = "INSERT INTO license_info (SoftwareTitle, SoftwareVersion, SoftwareVendor, SoftwareLastUpdate)";
query += " VALUES (";
// first is a true boolean statement
// if bool not first, then false
bool first = true;
// might not need this (foreach loop). if not listed first, add key + -#
foreach (string key in context.Request.Form.AllKeys)
{
// add comma (,) if not first
if (!first)
{
query += ", ";
}
query += "#" + key;
first = false;
}
// if not listed first, apply GETDATE() function
if (!first)
{
query += ", GETDATE());";
}
first = false;
SqlCommand command = new SqlCommand(query, connection);
foreach (string key in context.Request.Form.AllKeys)
{
command.Parameters.AddWithValue("#" + key, context.Request.Form[key]);
}
command.ExecuteNonQuery();
connection.Close();
// end connection
// connection.Close();
// redirect to admin
context.Response.Redirect(Properties.Settings.Default.BaseURL + #"/admin");
}

This can greatly be simplified by using the known form keys to get the values. There is no need for looping or building a dynamic query of any sort.
public void ProcessRequest(HttpContext context)
{
// create new Sql connection
const string query = "INSERT INTO license_info (SoftwareTitle, SoftwareVersion, SoftwareVendor, SoftwareLastUpdate) VALUES (#SoftwareTitle, #SoftwareVersion, #SoftwareVendor, #SoftwareLastUpdate)";
using(SqlConnection connection = new SqlConnection(Properties.Settings.Default.ConnectionString))
using(SqlCommand command = new SqlCommand(query, connection))
{
// todo: Update the SqlDbTypes and length according to your schema
command.Parameters.Add(new SqlParameter("#SoftwareTitle", SqlDbType.VarChar, 200)).Value = context.Request.Form["TitleKey"];
command.Parameters.Add(new SqlParameter("#SoftwareVersion", SqlDbType.VarChar, 200)).Value = context.Request.Form["VersionKey"];
command.Parameters.Add(new SqlParameter("#SoftwareVendor", SqlDbType.VarChar, 200)).Value = context.Request.Form["VendorKey"];
command.Parameters.Add(new SqlParameter("#SoftwareLastUpdate", SqlDbType.DateTime)).Value = DateTime.Now;
connection.Open();
command.ExecuteNonQuery();
}
Also you should use using blocks to wrap the types that implement IDisposable, in this case it will always ensure the database connection is closed even if there is an Exception.

Related

No value given for one or more required parameters. C#

I have a C# program and I made a code for subtracting the amount of sold products from the amount of these products in the stock (access datatable) so I used this code :
foreach (DataGridViewRow r in dataGridView1.Rows)
{
OleDbCommand command = new OleDbCommand("Update BookInserting set Amount = Amount - '" + Convert.ToInt32(r.Cells[1].Value) + "' where BookName = " + r.Cells[0].Value.ToString() + "", connection);
connection.Open();
command.ExecuteNonQuery();
connection.Close();
}
but when I run it gives me this error :
(No value given for one or more required parameters) .
I tried solving it several times but failed, I hope you can help me solve it.
Thanks in advance.
Your problem is probably caused by Access unable to recognize some part of your query as an object of the underlying table (or the table itself).
This problem and a more serious one called Sql Injection could be avoided using parameters. (And a side benefit your code becomes a lot clearer without all those strings concatenations)
So let's try to change your code in this way:
// String sql with parameters placeholders
string cmdText = #"Update BookInserting
set Amount = Amount - #amt
where BookName = #book";
connection.Open();
// Just build the command just one time outside the loop and
// add the two parameters required (without a value and in the exact order
// expected by the placeholders
OleDbCommand command = new OleDbCommand(cmdText, connection);
command.Parameters.Add("#amt", OleDbType.Integer);
command.Parameters.Add("#book", OleDbType.VarWChar);
// Inside the loop just change the parameters values and execute
foreach (DataGridViewRow r in dataGridView1.Rows)
{
// If the cell with the parameter for the WHERE
// clause is invalid skip the update
if(!r.IsNewRow && r.Cells[0].Value != null
&& r.Cells[0].Value.ToString() != "")
{
cmd.Parameters["#amt"].Value = Convert.ToInt32(r.Cells[1].Value);
cmd.Parameters["#book"].Value = r.Cells[0].Value.ToString();
command.ExecuteNonQuery();
}
}
connection.Close();
Final note. A connection object should be created each time you require it. From your code it is not clear if this is the case. Use the following pattern. (Using Statement)
using(OleDbConnection connection = new OleDbConnection(....))
{
... code that uses the connection ....
} // <- here the connection is closed and disposed

Insert new row to Database table if two fields does not match, otherwise sum value in certain column

I have data in Database table:
Here is the method for adding data:
public static void AddRecordToDatatable(string WindowTitle, int TimeSpent,
DateTime DateToday, string Project, string Username)
{
string sql = #"INSERT INTO dbo.Log (WindowTitle,TimeSpent,DateToday,Project,Username)" +
" VALUES (#WindowTitle,#TimeSpent,#DateToday,#Project,#Username)";
// Create the connection (and be sure to dispose it at the end)
using (SqlConnection cnn = new SqlConnection(DBconnectionString))
{
try
{
// Open the connection to the database.
// This is the first critical step in the process.
// If we cannot reach the db then we have connectivity problems
cnn.Open();
// Prepare the command to be executed on the db
using (SqlCommand cmd = new SqlCommand(sql, cnn))
{
// Create and set the parameters values
cmd.Parameters.Add("#WindowTitle", SqlDbType.NVarChar).Value = WindowTitle;
cmd.Parameters.Add("#TimeSpent", SqlDbType.Int).Value = TimeSpent;
cmd.Parameters.Add("#DateToday", SqlDbType.DateTime).Value = DateTime.Now.Date;
cmd.Parameters.Add("#Project", SqlDbType.NVarChar).Value = Project;
cmd.Parameters.Add("#Username", SqlDbType.NVarChar).Value = Username;
// Let's ask the db to execute the query
int rowsAdded = cmd.ExecuteNonQuery();
if (rowsAdded > 0)
{
//MessageBox.Show("Row inserted");
}
else
{
// This should never really happen, but let's leave it here
//MessageBox.Show("No row inserted");
}
}
cnn.Close();
}
catch (Exception ex)
{
// We should log the error somewhere,
// for this example let's just show a message
MessageBox.Show("ERROR:" + ex.Message);
}
}
}
How it is possible to check for existing record before inputting data to Database table and sum on certain value if it exists?
So basically check if WindowTitle = WindowTitle and DateToday = DateToday, if these two match, then take TimeSpent and sum it to existing TimeSpent in Database Table without inputting a new row.
I have tried to test ON DUPLICATE KEY UPDATE WindowTitle = #WindowTitle, DateToday = #DateToday after INSERT but Visual Studio is giving an error in Debugger for such a command pointing to ON (Incorrect syntax near ON). Also I am not sure if ON DUPLICATE is the best approach for this kind of case.
You need to expand your SQL to check for the existence of the record you think could exist.
IF EXISTS (SELECT 1 FROM dbo.Log WHERE WindowTitle = #WindowTitle AND DateToday = #DateToday)
BEGIN
--UPDATE HERE
END
ELSE
BEGIN
-- INSERT HERE
END
Alternatively you can create a query method and call that first, before calling AddRecordToDatatable
Personally I would do all of these CRUD operations using an ORM such as EF Core or preferably, NHibernate. But this all depends on requirements, limitations etc.

Return on my function doesn't work if using sql

I am populating a text box with the returned value of a function, but it doesn't work if I run the sql code inside the function. I can remove the sql related code and it works. so i'm stumped.
And by "doesn't work" i mean that the text box never gets populated with anything. it remains blank.
thanks
public string CreateResident()
{
string result = "hmm";
SqlConnection sqlConnection = new SqlConnection("Server=DELLXPS\\SQLEXPRESS; Initial Catalog=Warren_SEINDATASYSTEMS; Integrated Security=true;");
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "INSERT INTO [dbo].[NewUsers]([ResidentAccountNumber],[ResidentName],[ResidentAddress],[NumberOfVisitors],[TempPass],[Role])VALUES(#ResidentAccountNumber,#ResidentName,#ResidentAddress,#NumberOfVisitors,(select cast((Abs(Checksum(NewId()))%10) as varchar(1)) + char(ascii('a')+(Abs(Checksum(NewId()))%25)) + char(ascii('A')+(Abs(Checksum(NewId()))%25)) + left(newid(),5)),'resident')";
cmd.CommandType = CommandType.Text;
cmd.Connection = sqlConnection;
SqlParameter ResidentAccountNumber = new SqlParameter();
ResidentAccountNumber.ParameterName = "#ResidentAccountNumber";
ResidentAccountNumber.Value = txtboxResidenetAccountNumber.Text.Trim();
cmd.Parameters.Add(ResidentAccountNumber);
SqlParameter ResidentName = new SqlParameter();
ResidentName.ParameterName = "#ResidentName";
ResidentName.Value = txtboxResidentName.Text.Trim();
cmd.Parameters.Add(ResidentName);
SqlParameter ResidentAddress = new SqlParameter();
ResidentAddress.ParameterName = "#ResidentAddress";
ResidentAddress.Value = txtboxResidentAddress.Text.Trim();
cmd.Parameters.Add(ResidentAddress);
SqlParameter NumberOfVisitors = new SqlParameter();
NumberOfVisitors.ParameterName = "#NumberofVisitors";
NumberOfVisitors.Value = txtboxNumberOfVisitors.Text.Trim();
cmd.Parameters.Add(NumberOfVisitors);
try
{
sqlConnection.Open();
result = (string)cmd.ExecuteScalar();
sqlConnection.Close();
}
catch (Exception ex)
{
result = ex.Message;
}
return result;
}
protected void btnCreateResident_Click(object sender, EventArgs e)
{
txtboxTempPassword.Text = CreateResident();
}
Your SQL is wrong and you have a lot of problems but I want to show you a way to make your code more readable. Format it like this:
cmd.CommandText = #"INSERT INTO [dbo].[NewUsers] ([ResidentAccountNumber],[ResidentName],[ResidentAddress], NumberOfVisitors],[TempPass], Role])
VALUES(
#ResidentAccountNumber,
#ResidentName,
#ResidentAddress,
#NumberOfVisitors,
(select cast((Abs(Checksum(NewId()))%10) as varchar(1)) + char(ascii('a')+(Abs(Checksum(NewId()))%25)) + char(ascii('A')+(Abs(Checksum(NewId()))%25)) + left(newid(),5)),
'resident')";
cmd.CommandType = CommandType.Text;
cmd.Connection = sqlConnection;
We know that a select in a VALUES constructor is not legal so that is one problem.
Also having a SELECT without a from seems strange -- did you copy your code correctly?
You are using ExecuteScalar -- do you know what that does? It shouldn't include a query that includes INSERT query.
I'm guessing you probably want a stored procedure.
I would suggest do not write query in C# code, you must use Stored Procedure for the same purpose.
If you want your query to return some id, primary key or some value then you must write query for that after your insert query.
you can use the following keywords in your select query,if you want to return id from table.
SCOPE_IDENTITY returns the last IDENTITY value inserted into an IDENTITY column in the same scope.
IDENT_CURRENT returns the last identity value generated for a specific table in any session and any scope.
##IDENTITY returns the last identity value generated for any table in the current session, across all scopes.
If you want to return only one record then use ExecuteScalar else you can use ExecuteReader.
If your only purpose is to insert data into the table then you should use ExecuteNonQuery.
With the help of comments I went with ExecuteReader instead of the ExecuteScaler. And changed the statement to return a value
INSERT INTO [table] ([fields]) OUTPUT Inserted.MyColumn VALUES(values)
C# Code:
reader = cmd.ExecuteReader();
try
{
while (reader.Read())
{
result = reader[0].ToString();
}
}
catch (Exception ex)
{
result = ex.Message;
}
return result;

Insert query inside for loop not working correctly

I am working on Asp .Net project. So I have a page where I am generating random coupon keys. So user enters quantity and generate.
So what I did, I put a for loop according to quantity and inside the for loop I created a random key and search for the key in DB (key is unique) and then insert the data in DB.
Code:
for (int i = 0; i < quantity; i++)
{
do
{
couponKey = generateKey();
} while (!SearchEpinKey(couponKey));
conn = new SqlConnection(connString);
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
string query = "INSERT INTO CouponStock (coupon_key, status, created_on)";
query += "VALUES('" + couponKey + "','Unused', GETDATE())";
cmd.CommandText = query;
cmd.ExecuteNonQuery();
}
conn.Close();
}
Inside the loop, Flow is like:
-Genrate random key
-Search if random key exists or not in db
-If found similar then again generate random key
-Insert the data in DB
So when I run this page for smaller quantities like 10 or 15, its working fine. But when I go for 50 or 100 its inserting random number of rows like sometime 24, sometime 48. And application get hangs after this. I am thinking that Sql server is hitting numerous time in short interval. Any guidance on how to handle this will be helpful.
The only reason I could find is because of this
do
{
couponKey = generateKey();
} while (!SearchEpinKey(epinKey));
If you are using couponKey in your INSERT query, why do you use SearchEpinKey(epinKey)? where are you searching for couponKey in DB?
You are assigned generateKey() to couponKey variable, and your are checking against epinKey, I believe that when epinKey is already stored in DB it hangs (an infinite loop), because epinKey is always the same even if you assing a new value to couponKey
just change this line
} while (!SearchEpinKey(epinKey));
to this
} while (!SearchEpinKey(couponKey));
First thing first I think we should avoid opening a new connection on every insert, also we should always use ASP.Net build in function for parameter (e.g. AddWithValue), as they help avoid SQL Injection
var couponList = new System.Collections.Generic.List<String>();
var query = "INSERT INTO CouponStock(coupon_key, status, created_on) VALUES(#coupon_key, #status, GETUTCDATE());";
using (SqlConnection conn = new SqlConnection(connString))
{
try
{
conn.Open();
do{
var couponKey = generateKey();
//return early for readability
if(!SearchEpinKey(couponKey)) continue;
if(couponList.Contains(couponKey)) continue;
//add to coupon list to ensure newly generated key does not duplicate
couponList.Add(couponKey);
SqlCommand cmd = conn.CreateCommand(query);
cmd.Parameters.AddWithValue("#coupon_key", couponKey);
cmd.Parameters.AddWithValue("#status", "Unused");
cmd.ExecuteNonQuery();
}
while (couponList.Count < quantity);
}
catch (Exception e)
{
// handle exceptions or re-throw them...
}
finally
{
conn.Close();
}
}

how to update a table using oledb parameters?

I am having a table which has three fields, namely LM_code,M_Name,Desc. LC_code is a autogenerated string Id, keeping this i am updating M_Name and Desc. I used normal update command, the value is passing in runtime but the fields are not getting updated. I hope using oledb parameters the fields can be updated.
Here is my code.
public void Modify()
{
String query = "Update Master_Accounts set (M_Name='" + M_Name + "',Desc='" + Desc + "') where LM_code='" + LM_code + "'";
DataManager.RunExecuteNonQuery(ConnectionString.Constr, query);
}
In DataManager Class i am executing the query string.
public static void RunExecuteNonQuery(string Constr, string query)
{
OleDbConnection myConnection = new OleDbConnection(Constr);
try
{
myConnection.Open();
OleDbCommand myCommand = new OleDbCommand(query, myConnection);
myCommand.ExecuteNonQuery();
}
catch (Exception ex)
{
string Message = ex.Message;
throw ex;
}
finally
{
if (myConnection.State == ConnectionState.Open)
myConnection.Close();
}
}
private void toolstModify_Click_1(object sender, EventArgs e)
{
txtamcode.Enabled = true;
jewellery.LM_code = txtamcode.Text;
jewellery.M_Name = txtaccname.Text;
jewellery.Desc = txtdesc.Text;
jewellery.Modify();
MessageBox.Show("Data Updated Succesfully");
}
This annoyed me, screwy little OleDB, so I'll post my solution here for posterity. It's an old post but seems like a good place.
OleDB doesn't recognize named parameters, but it apparently does recognize that you're trying to convey a named parameter, so you can use that to your advantage and make your SQL semantic and easier to understand. So long as they're passed in the same order, it'll accept a variable as a named parameter.
I used this to update a simple Access database in a network folder.
using (OleDbConnection conn = new OleDbConnection(connString))
{
conn.Open();
OleDbCommand cmd = conn.CreateCommand();
for (int i = 0; i < Customers.Count; i++)
{
cmd.Parameters.Add(new OleDbParameter("#var1", Customer[i].Name))
cmd.Parameters.Add(new OleDbParameter("#var2", Customer[i].PhoneNum))
cmd.Parameters.Add(new OleDbParameter("#var3", Customer[i].ID))
cmd.Parameters.Add(new OleDbParameter("#var4", Customer[i].Name))
cmd.Parameters.Add(new OleDbParameter("#var5", Customer[i].PhoneNum))
cmd.CommandText = "UPDATE Customers SET Name=#var1, Phone=#var2" +
"WHERE ID=#var3 AND (Name<>#var4 OR Phone<>#var5)";
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
}
}
It may look like an excess of code, and yes you're technically repeating yourself, but this makes it worlds easier when you're playing connect-the-dots later on.....
You are close with the rest of your connection and such, but as you note, doing it with parameterized queries is safer from SQL-Injection...
// Some engines used named parameters, others may not... The "?"
// are "place-holders" for the ordinal position of parameters being added...
String MyQuery = "Update MyTable set SomeField = ?, AnotherField = ? "
+ " where YourKeyField = ?";
OleDbCommand MyUpdate = new OleDbCommand( MyQuery, YourConnection );
// Now, add the parameters in the same order as the "place-holders" are in above command
OleDbParameter NewParm = new OleDbParameter( "ParmForSomeField", NewValueForSomeField );
NewParm.DbType = DbType.Int32;
// (or other data type, such as DbType.String, DbType.DateTime, etc)
MyUpdate.Parameters.Add( NewParm );
// Now, on to the next set of parameters...
NewParm = new OleDbParameter( "ParmForAnotherField", NewValueForAnotherField );
NewParm.DbType = DbType.String;
MyUpdate.Parameters.Add( NewParm );
// finally the last one...
NewParm = new OleDbParameter( "ParmForYourKeyField", CurrentKeyValue );
NewParm.DbType = DbType.Int32;
MyUpdate.Parameters.Add( NewParm );
// Now, you can do you
MyUpdate.ExecuteNonQuery();
Just to add to RJB's answer, it's a little-known fact that OleDb actually DOES accept named parameters. You've just got to declare the parameters in SQL as well.
See: low-bandwidth.blogspot.com.au/2013/12/positional-msaccess-oledb-parameters.html
If you DON'T declare the parameters in SQL, OleDb uses purely positional parameter insertion, and it doesn't matter if the names of the parameters match the SQL, or if parameters are used twice in the SQL - it will just go through and blindly replace any found parameters in the SQL in order from start to end, with those passed.
However if you DO declare the parameters correctly, you get the benefit of named parameters and parameters allowed to be repeated multiple times within the SQL statement.

Categories

Resources