I am inserting a web form into the database and so using parameterized queries. I have a CheckBoxList. How can I iterate over CheckBoxList, create an insert statement for every checked thing (many-to-many), and keep this parameterized and executed in one swoop?
I have this right now:
string query = "INSERT INTO resources (url, submitted_by, author_name) VALUES (#url, #submitted_by, #author_name);";
foreach (ListItem li in CheckBoxList1.Items)
{
if (li.Selected = true)
{
query += "; INSERT INTO ";
}
}
SqlCommand cmd = new SqlCommand(query, conn);
cmd.Parameters.AddWithValue("#url", TextBox1.Text);
cmd.Parameters.AddWithValue("#submitted_by", TextBox2.Text);
cmd.Parameters.AddWithValue("#author_name", TextBox3.Text);
try
{
conn.Open();
cmd.ExecuteNonQuery();
Label1.Text = "Added to database.";
}
As you can see it's unfinished. Any suggestions?
You could use LINQ to generate unique named parameters for each item in your collection, then add the associated values in later:
var builder = new StringBuilder();
var listParams = CheckBoxList1.Items
.Where(li => li.Selected)
.Select(li, idx => new
{
PhoneString = String.Format("#phone_id{0}", idx),
PhoneValue = GetPhoneId(li),
ResourceString = String.Format("#resource_id{0}", idx),
ResourceValue = GetResourceId(li)
};
foreach (var param in listParams)
{
builder.AppendFormat("INSERT INTO phones_resources (phone_id, resource_id)
VALUES ({0}, {1});",
param.PhoneString, param.ResourceString);
}
SqlCommand cmd = new SqlCommand(builder.ToString(), conn);
foreach (var param in listParams)
{
cmd.Parameters.AddWithValue(param.PhoneString, param.PhoneValue);
cmd.Parameters.AddWithValue(param.ResourceString, param.ResourceValue);
}
I'm assuming you have some way of getting associated phone_id, resource_id from any given ListItem - you can just plug that in where I've put the placeholder Get___ functions.
Note: Switched to a StringBuilder - it's much better than building up a string with repeated +=.
Related
I am trying to send values to a stored procedure via a listbox containing country names I get them from the database. If I choose one option code works 100% 100%. But if I put 2 or 3 options, I get the following error:
Parameter '#stIdCity' was supplied multiple times.
Line 322: da.Fill(ds);
Full code:
protected void lstBoxTestCity_SelectedIndexChanged(object sender, EventArgs e)
{
string str = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
using (SqlConnection con = new SqlConnection(str))
{
using (SqlCommand cmd = new SqlCommand("Tprocedure", con))
{
cmd.CommandType = CommandType.StoredProcedure;
foreach (ListItem item in lstBoxTestCity.Items)
{
if (item.Selected)
{
cmd.Parameters.AddWithValue("#stIdCity", item.Value);
}
}
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds);
gvProducts.DataSource = ds;
gvProducts.DataBind();
}
}
}
Stored procedure:
CREATE PROCEDURE Tprocedure
(#stIdCity NVARCHAR(20) = NULL, )
AS
BEGIN
SELECT *
FROM employees
INNER JOIN TCity ON employees.IdstICity = TCity.IdstICity
WHERE (employees.IdstICity IN (SELECT ITEM
FROM dbo.SplitString(#stIdCity, ','))
OR ISNULL(#stIdCity, '') = '')
END
Image of list:
Please help to solve the problem.
Sorry if the question was repeated, but I could not find a solution to it.
You have this:
foreach (ListItem item in lstBoxTestCity.Items)
{
if (item.Selected)
{
cmd.Parameters.AddWithValue("#stIdCity", item.Value);
}
}
so for each selected city, you add a new parameter. That is why you get that error message.
Apparently you want one parameter, with a comma-separated value. So build that:
var value = string.Join(",",lstBoxTestCity.Items.Where(it => it.IsSelected).Select(it => it.Value));
cmd.Parameters.Add("#stIdCity", SqlDbType.NVarChar, 20).Value = value;
First I use LINQ to filter for selected items, then I get their value. string.Join combines the resulting values with commas.
But do note that your stored procedure accepts just a nvarchar(20), so you may run out of space when multiple cities have been selected.
You ONLY have the ONE parmater, and it is a comma delimited string of values.
So, you have to "build up" a comma delimited string. Say like this:
string myparms = "";
foreach (ListItem item in lstBoxTestCity.Items)
{
if (item.Selected)
{
if (myparms != "")
myparms += ",";
myparms += item.Value;
}
}
cmd.Parameters.Add("#stIdCity",SqlDbType.NVarChar).Value = myparms;
So, you only passing one value - but you have to build up the "string" of values to pass.
I have a table which contains a single column "CODE_CAR".
I want to populate this table with list of string as below :
_Connection.Open();
var lst_code_cars = new List<string> { "YH_21", "NM_00", "BAR_N178" };
string cmdText = "INSERT INTO Y157.CARS_IDENDITY(CODE_CAR) VALUES ("+lst_code_cars+")";
MySqlCommand cmd = new MySqlCommand(cmdText, _Connection);
foreach (string cars_code in lst_code_cars )
{
cmd.Parameters.AddWithValue("#"+cars_code , cars_code );
cmd.ExecuteNonQuery();
}
_Connection.Close();
When i try this code, i get error : MySqlException : Parameter '#NM_00' must be defined.
The error tells you exactly what is wrong. You did not define a parameter, you concatenated the value directly into the query string. So don't do that.
_Connection.Open();
var lst_code_cars = new List<string> { "YH_21", "NM_00", "BAR_N178" };
string cmdText = "INSERT INTO Y157.CARS_IDENDITY(CODE_CAR) VALUES (#CarCode)";
MySqlCommand cmd = new MySqlCommand(cmdText, _Connection);
foreach (string cars_code in lst_code_cars )
{
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#CarCode" , cars_code );
cmd.ExecuteNonQuery();
}
_Connection.Close();
You'll also need to clear the parameters if you mean to re-add them each loop. Either that or add the parameter once and then edit the value in the loop instead of adding a new parameter each time.
Ideally, though, you could write this as a batch insert.
I want to insert each line of a Textbox lines to a row of database (line by line) when its TextMode property is Multiline using a foreach loop? I used this code but it inserts all the lines in one row of my "ChTB" table. What is wrong with it?
string ID = null;
DateTime RegDtTime = DateTime.UtcNow;
SqlConnection con1 = new SqlConnection(connectionString);
string sql1 = "SELECT * FROM ChTB";
SqlCommand command1 = new SqlCommand(sql1, con1);
con1.Open();
foreach (object line_loopVariable in this.Textbox1.Text.Split({ Environment.NewLine }, StringSplitOptions.None)) {
line = line_loopVariable;
ID = line;
string commandText = "insert into ChTB(ID,Visible,RegDtTime,LastDateTime) values(#ID,#Visible,#RegDtTime,#LastDateTime)";
SqlCommand cmdObj = new SqlCommand(commandText, con1);
cmdObj.Parameters.AddWithValue("#ID", ID);
cmdObj.Parameters.AddWithValue("#Visible", "NO");
cmdObj.Parameters.AddWithValue("#RegDtTime", RegDtTime);
cmdObj.Parameters.AddWithValue("#LastDateTime", RegDtTime);
cmdObj.ExecuteNonQuery();
}
con1.Close();
Why are you using three different variables for the same value? Just do this:
foreach (var line in myTextBox.Lines)
{
// Use line here.
}
As for the insert, don't create a new command object every time and add new parameters. Create one object, add the parameters and then set their Value properties each time, e.g.
var command = new SqlCommand("INSERT INTO MyTable (SomeColumn) VALUES (#SomeColumn)", connection)
var parameter = command.Parameters.Add("#SomeColumn", SqlDbType.VarChar, 50)
foreach (var line in myTextBox.Lines)
{
parameter.Value = line
// Execute command here.
}
I want to execute this command
select * from table1 where id='"+comboBox1.Text+"'and name='"+comboBox2.Text+"'
but I don't want them to be considered if they were empty, for example if combobox1.Text is empty, I want the command to be like
select * from table1 where name='"+comboBox2.Text+"'
You can have a check with the text boxes like
if (comboBox1.Text.Trim().Length == 0) {
// your query
}
else{
// other query
}
But you should be using parameterised queries. Building up your queries like that leaves you open to SQL injection.
So your SQL String should be
select * from table1 where id=#id and name=#name
And you add the parameters to the command object before executing the query.
You can build up your query as you go, and still protect yourself from SQL injection:
(I'm writing this from memory, it should compile...)
using (var conn = new SqlConnection("..."))
{
var sb = new StringBuilder("SELECT * FROM table1 WHERE");
using (var cmd = new SqlCommand { Connection = conn })
{
if (!String.IsNullOrEmpty(comboBox1.Text))
{
sb.Append(" id = #ID");
cmd.Parameters.AddWithValue("#ID", int.Parse(comboBox1.Text));
}
if (!String.IsNullOrEmpty(comboBox2.Text))
{
sb.Append(" name = #NAME");
cmd.Parameters.AddWithValue("#NAME", comboBox2.Text);
}
var query = sb.ToString();
cmd.CommandText = query.EndsWith("WHERE") ? query.Remove(query.Length - 5) : query;
conn.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
// do whatever you need to do with your data
}
}
}
}
Something to keep in mind...
This could get out of hand if you have a dozen if blocks instead of just two, and then start nesting more if blocks inside of those. Maintenance will become a nightmare.
It may just be easier, if you only have a few conditions, to just create a few separate queries and determine which one is called instead of building a query dynamically like this.
i found the solution i want
MySqlDataAdapter cmd = new MySqlDataAdapter("select * from table1 where (ID=#id or #id2) and (name=#name or #name2) ", co);
if (comboBox1.Text == "")
{
cmd.SelectCommand.Parameters.AddWithValue("#id", "1");
cmd.SelectCommand.Parameters.AddWithValue("#id2", "1");
}
else
{
cmd.SelectCommand.Parameters.AddWithValue("#id", comboBox1.Text);
cmd.SelectCommand.Parameters.AddWithValue("#id2", "0");
}
if (comboBox2.Text == "")
{
cmd.SelectCommand.Parameters.AddWithValue("#name", "1");
cmd.SelectCommand.Parameters.AddWithValue("#name2", "1");
}
else
{
cmd.SelectCommand.Parameters.AddWithValue("#name", comboBox1.Text);
cmd.SelectCommand.Parameters.AddWithValue("#name2", "0");
}
//thanks everyone for the help , i used all what u gave me to create this solution
I want to get the names associated with the states i select in my program. Below is the code that i currently have. My database has multiple locations within a state that have different contacts. I just want to select a state and acquire everyone under that state. Thanks for the help!
con = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=F:\\Database\\LocNo.accdb");
con.Open();
foreach (Object c in checkedListBox2.CheckedItems)
{
if (checkedListBox2.GetItemCheckState(checkedListBox2.Items.IndexOf(c)) == CheckState.Checked)
{
str1 += c.ToString() + ",";
flag = 1;
}
}
i = 0;
allSelectedtypestring = "";
allSelected = str1.Split(',');
while (allSelected.Length - 1 > i)
{
str = "select c1 from table where state ='" + allSelected[i++] + "'";
cmd = new OleDbCommand(str, con);
dr = cmd.ExecuteReader();
dr.Read();
allSelectedtypestring += dr.GetString(11);
}
label30.Text = Convert.ToString(allSelectedtypestring);
con.Close();
You can use the following code to retrieve the contacts:
var states = new List<string>();
foreach (Object c in checkedListBox2.CheckedItems)
{
states.Add(c.ToString());
flag = 1; // Can also be substituted by states.Count > 0
}
using(var con = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=F:\\Database\\LocNo.accdb"))
{
con.Open();
using(var cmd = con.CreateCommand())
{
var paramIndex = 0;
var paramClause = new System.Text.StringBuilder(100);
foreach(var state in states)
{
if (paramClause.Length > 0)
paramClause.Append(", ");
paramClause.Append("?");
var paramName = "State" + (paramIndex++).ToString();
cmd.Parameters.AddWithValue(paramName, state);
}
var paramsClause = string.Join(", ", cmd.Parameters.
cmd.CommandText = "select distinct c1 from table where state IN (" + paramsClause.ToString() + ")";
using(var rdr = cmd.ExecuteReader())
{
var contacts = new List<string>();
while(rdr.Read())
{
contacts.Add(rdr.GetString(0);
}
label30.Text = string.Join(", ", contacts);
}
}
}
Please note that I've made the following changes:
Added using statements to reliably dispose the connection, command and reader.
Used a List<string> as a more convenient way to collect the selected states.
Added DISTINCT to the SELECT in order to filter duplicate entries.
Used a parameter in the command text in order to avoid SQL injection attacks. Though this way to use a parameter with an IN clause works for SQL Server, I haven't checked whether it also works for an Access database. Let me know in the comments if it doesn't work.