error executing procedure in the loop - c#

I'm facing problem while executing procedure inside loop. My requirment is to insert data to table. first column is alphabet which I try to get in loop and next three are common for all rows. But I'm getting error after first iteration saying that variable must be unique.
string str = Properties.Settings.Default.con;
SqlConnection con = new SqlConnection(str);
char[] az = Enumerable.Range('A', 'Z' - 'A' + 1).Select(i => (Char)i).ToArray();
SqlCommand cmd = new SqlCommand("Execute InsertPurchase #ShipTo,#StoreName,#desc,#Alpha", con);
try
{
cmd.Parameters.AddWithValue("#ShipTo", txtstoreto.Text);
cmd.Parameters.AddWithValue("#StoreName", txtstorefrom.Text);
cmd.Parameters.AddWithValue("#desc", Globals.s_desc.ToString());
con.Open();
foreach (var c in az)
{
cmd.Parameters.AddWithValue("#Alpha", c.ToString());
cmd.ExecuteNonQuery();
}
con.Close();
j = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Error message:
The variable name '#Alpha' has already been declared. Variable names must be unique within a query batch or stored procedure.

You're adding parameter Alpha in the loop over and over again.
So instead of adding it in the loop you just have to set its value:
cmd.Parameters.Add("#Alpha", SqlDbType.VarChar);
con.Open();
foreach (var c in az)
{
cmd.Parameters["#Alpha"].Value = c.ToString();
cmd.ExecuteNonQuery();
}
Also notice that using of AddWithValue is not a good idea in most cases since it tries to deduce parameter type from the value passed. In most cases it is better to explicitly set parameter type.

I think you keep on adding the #Alpha parameter over and over...
First time add the parameter and set the value.
Every subsequent iteration, just set the value.
eg first iteration....
cmd.Parameters.AddWithValue("#Alpha", c.ToString());
Subsequent iterations....
cmd.Parameters["#Alpha"].Value = c.ToString();
Good luck.

As an alternative, you can move all parameter declarations in your for loop and Clear them in every iteration.
foreach (var c in az)
{
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#ShipTo", txtstoreto.Text);
cmd.Parameters.AddWithValue("#StoreName", txtstorefrom.Text);
cmd.Parameters.AddWithValue("#desc", Globals.s_desc.ToString());
cmd.Parameters.AddWithValue("#Alpha", c.ToString());
cmd.ExecuteNonQuery();
}
But of course, this adds unnecessary first 3 parameter in every iteration and clear them again.
By the way, don't use AddWithValue as much as you can. It may generate unexpected and surprising results sometimes. Use Add method overload to specify your parameter type and it's size.
Also use using statement to dispose your connection and command automatically instead of calling Close or Dispose methods manually.

You have to clear the variable to add again Something like this:
foreach (var c in az)
{
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#ShipTo", txtstoreto.Text);
cmd.Parameters.AddWithValue("#StoreName", txtstorefrom.Text);
cmd.Parameters.AddWithValue("#desc", Globals.s_desc.ToString());
cmd.Parameters.AddWithValue("#Alpha", c.ToString());
cmd.ExecuteNonQuery();
}

Related

Foreach loop with if statement ERROR

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.

The variable name '#product_Id' has already been declared. Variable names must be unique within a query batch or stored procedure

i insert the data from datagridview to database table. but the error that come in the exception i write in the title. the value save in the database table as well.
private void btnSavePurchases_Click(object sender, EventArgs e)
{
if ((string.IsNullOrEmpty(txtPGrandTotal.Text)) || (txtPGrandTotal.Text == "0"))
{
MessageBox.Show("No record available");
}
else
{
try
{
string query1 = "INSERT INTO purchases (productId) values (#product_Id)";
command = DBConnectivity.getCommandForQuery(query1, connection);
for (int i = 0; i < dGvPurchases.Rows.Count; i++)
{
// command.Parameters.Clear();
command.Parameters.AddWithValue("product_Id", dGvPurchases.Rows[i].Cells[9].Value);
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
}
}
}
Probably
AddWithValue("product_Id"...
should be
AddWithValue("#product_Id"...
By the way, since you add your parameters in a for loop, you need clear them top of your for loop. That's why you need to uncomment your command.Parameters.Clear() part. Without Clear method, you try to add same parameter name over and over again to same command and you will get an error in your second iteration.
As an another solution, you can declare your parameter name outside of your loop and add it's value inside of loop.
And don't use AddWithValue as much as possible. It may generate unexpected results sometimes. Use .Add() overloads to specify your db type and it's size.
Don't forget to use using statement as well to dispose your command object.
I don't see command anywhere in the scope here. Probably it is a class variable. That is one part of your issue.
Move the construction of the command variable to inside your method:
var command = DBConnectivity.getCommandForQuery(...);
^^^
Second, your for look causes issues. You can't add the same parameter over and over again. If you want to insert multiple rows, add multiple insert statements. Create the command inside the for loop.
problem is that command.Parameters.AddWithValue adds the parameter to the list of parameters. you can't add a parameter twice. since you call the command several times, define the parameter outside and set value within the loop:
string query1 = "INSERT INTO purchases (productId) values (#product_Id)";
command = DBConnectivity.getCommandForQuery(query1, connection);
// Define the Parameter just once
var param = command.Parameters.Add("#product_Id", SqlDbType.Int);
for (int i = 0; i < dGvPurchases.Rows.Count; i++)
{ // set value while looping
param.Value = dGvPurchases.Rows[i].Cells[9].Value;
command.ExecuteNonQuery();
}

Read records from one table , and write to another table. Exception occurs procedure or function has too many arguments specified?

I am trying to retrieve list of records from one table , and write to another table. I've used a simple query to retrieve the values to SqlDataReader,then load them to a DataTable. Using the DataTableReader , I am going through the entire data set which is Saved in DataTable. The problem is, while reading each and every record I am trying to insert those values to another table using a Stored Procedure.But it only insert the first row of values,and for the second row onward giving some Exception saying."procedure or function has too many arguments specified".
string ConStr = ConfigurationManager.ConnectionStrings["ConString"].ConnectionString;
SqlConnection NewCon = new SqlConnection(ConStr);
NewCon.Open();
SqlCommand NewCmd3 = NewCon.CreateCommand();
NewCmd3.CommandType = CommandType.Text;
NewCmd3.CommandText ="select * from dbo.Request_List where group_no ='" +group_no+ "'";
NewCon.Close();
NewCon.Open();
SqlDataReader dr = (SqlDataReader)NewCmd3.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(dr);
DataTableReader reader = new DataTableReader(dt);
NewCmd.Dispose();
NewCon.Close();
NewCon.Open();
SqlCommand NewCmdGrpReqSer = NewCon.CreateCommand();
NewCmdGrpReqSer.CommandType = CommandType.StoredProcedure;
NewCmdGrpReqSer.CommandText = "Voucher_Request_Connection";
if (reader.HasRows)
{
int request_no = 0;
while (reader.Read())
{
request_no = (int)reader["request_no"];
NewCmdGrpReqSer.Parameters.Add("#serial_no", serial_no);
NewCmdGrpReqSer.Parameters.Add("#request_no", request_no);
try
{
NewCmdGrpReqSer.ExecuteNonQuery();
MessageBox.Show("Connection Updated");//just to check the status.tempory
}
catch (Exception xcep)
{
MessageBox.Show(xcep.Message);
}
MessageBox.Show(request_no.ToString());//
}
NewCmdGrpReqSer.Dispose();
NewCon.Close();
}
Any Solutions ?
As #Sparky suggests, the problem is that you continue to add parameters to the insertion command. There are several other ways in which the code could be improved, however. These improvements would remove the need to clear the parameters and would help to make sure you don't leave disposable resources undisposed.
First - use the using statement for your disposable objects. This removes the need for the explicit Close (btw, only one of Close/Dispose is needed for the connection as I believe Dispose calls Close). Second, simply create a new command for each insertion. This will prevent complex logic around resetting the parameters and, possibly, handling error states for the command. Third, check the results of the insertion to make sure it succeeds. Fourth, explicitly catch a SqlException - you don't want to accidentally hide unexpected errors in your code. If it's necessary to make sure all exceptions don't bubble up, consider using multiple exception handlers and "doing the right thing" for each case - say logging with different error levels or categories, aborting the entire operation rather than just this insert, etc. Lastly, I would use better variable names. In particular, avoid appending numeric identifiers to generic variable names. This makes the code harder to understand, both for others and for yourself after you've let the code sit for awhile.
Here's my version. Note there are several other things that I might do such as make the string literals into appropriately named constants. Introduce a strongly-typed wrapper around the ConfigurationManager object to make testing easier. Remove the underscores from the variable names and use camelCase instead. Though those are more stylistic in nature, you might want to consider them as well.
var connectionString = ConfigurationManager.ConnectionStrings["ConString"].ConnectionString;
using (var newConnection = new SqlConnection(connectionString))
{
newConnection.Open();
using (var selectCommand = newConnection.CreateCommand())
{
selectCommand.CommandType = CommandType.Text;
select.CommandText ="select request_no from dbo.Request_List where group_no = #groupNumber";
selectCommand.Parameters.AddWithValue("groupNumber", group_no);
using (dataReader = (SqlDataReader)newCommand.ExecuteReader())
{
while (reader.HasRows && reader.Read())
{
using (var insertCommand = newConnection.CreateCommand())
{
insertCommand.CommandType = CommandType.StoredProcedure;
insertCommand.CommandText = "Voucher_Request_Connection";
var request_no = (int)reader["request_no"];
insertCommand.Parameters.Add("#serial_no", serial_no);
insertCommand.Parameters.Add("#request_no", request_no);
try
{
if (insertCommand.ExecuteNonQuery() == 1)
{
MessageBox.Show("Connection Updated");//just to check the status.tempory
}
else
{
MessageBox.Show("Connection was not updated " + request_no);
}
}
catch (SqlException xcep)
{
MessageBox.Show(xcep.Message);
}
MessageBox.Show(request_no.ToString());//
}
}
}
}
}
Try clearing your parameters each time...
while (reader.Read())
{
request_no = (int)reader["request_no"];
// Add this line
NewCmdGrpReqSer.Parameters.Clear();
NewCmdGrpReqSer.Parameters.Add("#serial_no", serial_no);
NewCmdGrpReqSer.Parameters.Add("#request_no", request_no);
try
{

Problems when adding data into database (More data/parameters added)

I have a database which i insert some data into. The problem i have is that it will add more data then it should.
When the logfordb.pos is increasing it will add all prevoius data one more time and next time it will add even more.
Here is the code... with some reduction of conn string and parametres.
public void WriteToDatabase(DatabaseLog logfordb)
{
conn.Open();
var cmd = new SqlCommand();
cmd.CommandText = "Insert INTO [X] (Y, Z, C " + "Values (#val1, #val2, #val3";
for (int index = 0; index < logfordb.ListofDB[logfordb.pos].X.Count; index++)
{
cmd.Connection = conn;
cmd.Parameters.Clear();
if (logfordb.ListofDB[logfordb.pos].X[index].Details != null)
{
cmd.Parameters.AddWithValue("#val1", logfordb.ListofDB[logfordb.pos].X[index].Details.Age);
cmd.Parameters.AddWithValue("#val2", logfordb.ListofDB[logfordb.pos].X[index].Details.Name);
cmd.Parameters.AddWithValue("#val3", logfordb.ListofDB[logfordb.pos].X[index].Details.Etc);
cmd.ExecuteNonQuery();
}
}
conn.Close();
logfordb.pos += 1;
}
It seems like you want to change the value of logfordb.pos I think you want to pass logfordb by ref. I suspect the value isn't changing and the second time you call this function you end up adding the same data again because the value outside of this scope isn't being changed just a local copy that the function sees (and then exists destroying references to the changes).
Your code iterates through the entire logfordb every single time this is called.
You have three options:
At the end of this method, clear the logfordb entirely. (probably makes the most sense)
Add a starting position parameter, and let the calling code decide the position to start pushing the logfordb records into the database.
Add a "Stored" property to each record inside logfordb so that you can set it when that individual record has been stored and not push that record again.
Out of those, option 1 is probably your safest bet.

Calling a stored procedure using C#

private void btnGo_Click(object sender, EventArgs e)
{
Array IDlist = txtUserID.Text.Split(new char[] { });
ArrayList badID = new ArrayList();
foreach (string textLine in IDlist)
{
try
{
int LineID = Convert.ToInt32(textLine);
string emp = txtDistricts.Text;
command.Parameters.Add("#EmpID", SqlDbType.Int).Value = LineID;
if (!emp.Equals(string.Empty))
command.Parameters.Add("#SchoolDistricts", SqlDbType.NVarChar).Value = emp;
else command.Parameters.Add("#SchoolDistricts", SqlDbType.NVarChar).Value = DBNull.Value;
if (cbRemove.Checked)
command.Parameters.Add("#Options", SqlDbType.Int).Value = 1;
else if (cbReset.Checked)
command.Parameters.Add("#Options", SqlDbType.Int).Value = 0;
else command.Parameters.Add("#Options", SqlDbType.Int).Value = DBNull.Value;
SqlParameter returnValue = new SqlParameter("#return_value", DbType.String);
returnValue.Direction = ParameterDirection.ReturnValue;
command.Parameters.Add(returnValue);
conn.Open();
command.Connection = conn;
// command.ExecuteNonQuery();
command.ExecuteScalar();
String OutPutCheck = (command.Parameters["#return_value"].Value.ToString());
String getCheck = (command.ExecuteScalar().ToString());
OPBox.Text += LineID + "--->>" + OutPutCheck + "--->>" + getCheck + "\n";
conn.Close();
//flagUser(LineID, emp);
}
catch (Exception ex)
{
//stored procedure error
badID.Add(textLine);
conn.Close();
}
}}
I made an APP , which takes bunch of ID at a time. After btn_click these values put in array. Then from array each ID pass to store procedure one by one, and get return value. well First value give return value, but after that when second value pass to store procedure it gives following error.
> ERROR::::ex = {"Procedure or function
> usp_Flag_Employee has too many
> arguments specified."}
You keep adding parameters to your command object without reseting it. You should move your connection and command objects into the method where they are being called and use 'using' statements.
Because your connection and command are class fields, each instance of the loop is re-adding the parameters to the old set of parameters. At minimum, reset the parameters collection at the top of the loop.
You are passing too many parameters to the procedure. If you paste the procedure code we can help identify, however just do a count of the params and check to ensure you have all defined in the proc.
I don't see any code generating the SqlCommand object in your example.
If command is local to the class, there's a very good chance that it has already been used (which means it probably already has parameters added to it).
I also see no code that sets the command type to StoredProcedure. Depending on what the command text is, this could be the issue as well (if you're simply passing the stored procedure name without setting the type...it will see the command as having no parameters).
Re-Writing the code to use its own SqlConnection and SqlCommand would make this much easier to debug (unless, of course, it already is and you didn't give us that code).
EDIT
I just noticed that you're using the code inside a foreach loop without clearing the parameters. That is yet another issue (and probably the most likely cause for this issue). Just be sure to call command.Parameters.Clear() at the beginning of each loop before adding the new parameters.
Call the Clear method before adding the parameters.
It works the first time because your command object has no parameters. For each subsequent iteration you keep on adding another set of parameters to your command object.
You need to clear the parameters for your command object on each iteration.

Categories

Resources