Use switch statement inside foreach? - c#

in this scenario I'm dealing with two gridviews (gvSnacks and gvSnackCart). gvSnacks contains a fixed list of items with a template button column to "add" the item to gvSnackCart.
The foreach comes into play because I want to make sure that the item hasn't already been placed in the cart. If the user selects an item twice, I want to execute a sql command and make the gvSnackCart quantity of that item go up by 1 (instead of generating a new record).
If the record isn't in the gvSnackCart, the foreach should loop all the way to its default and insert it.
Here's the sql command to insert
string sqlInsert = "INSERT INTO ShoppingCart…
([shopperID], [itemName], [itemType], [quantityOrdered], [unitPrice])…
VALUES (#shopperID, #itemName, #itemType, #quantityOrdered, #unitPrice)";
myCommand.Parameters.Add("#shopperID", System.Data.SqlDbType.NVarChar).Value = txtCurrentUser.Text;
myCommand.Parameters.Add("#itemName", System.Data.SqlDbType.NVarChar).Value = snackDescription;
myCommand.Parameters.Add("#itemType", System.Data.SqlDbType.NVarChar).Value = "snack";
myCommand.Parameters.Add("#quantityOrdered", System.Data.SqlDbType.Int).Value = 1;
myCommand.Parameters.Add("#unitPrice", System.Data.SqlDbType.Money).Value = snackPrice;`
SqlCommand myCommand = new SqlCommand(sqlInsert, myConnection);`
Now I get the selected item's name from gvSnacks, and compare with each one present inside gvSnackCart
// Retrieve item's name
int index = Convert.ToInt32(e.CommandArgument);
GridViewRow row = gvSnacks.Rows[index];
string snackDescription = row.Cells[2].Text.ToString();
// Checks for duplicate snack listing
int duplicate = 0;
foreach (GridViewRow gvRowSnack in gvSnackCart.Rows)
{
if (gvRowSnack.Cells[1].Text.ToString() == snackDescription)
{
duplicate = 1;
}
switch (duplicate)
{
case 0:
break;
case 1:
//This is the case where it's a duplicate
//and will be a command to update the record
break;
default:
try
{
myConnection.Open();
myCommand.ExecuteNonQuery();
myConnection.Close();
}
catch (Exception ex)
{
lblError.Visible = true;
lblError.Text = "There was an error while adding the records" + "<br />" + ex.Message;
}
break;
}
}
The gvSnackCart is databound and should update with a new item, unless its a duplicate where it'd do an update. For some reason however it is not functioning. I am not getting an error at runtime. I have tested a read/write with a label so I know I'm retrieving the item name from gvSnacks. I'm also checking the database table and it's not showing any records being added. What seems wrong?

You can spare yourself a lot of hassle by using LINQ:
foreach (var snacks in gvSnackCart.Rows.GroupBy(snack => snack.Cells[1].Text.ToString()))
{
var p = myCommand.Parameters;
// I'm largely guessing at these, but hopefully you get the idea.
p.Add("#shopperID" , SqlDbType.NVarChar).Value = txtCurrentUser.Text;
p.Add("#itemName" , SqlDbType.NVarChar).Value = snacks.Key;
p.Add("#itemType" , SqlDbType.NVarChar).Value = /* ??? */;
p.Add("#quantityOrdered", SqlDbType.Int ).Value = snacks.Count();
p.Add("#unitPrice" , SqlDbType.Money ).Value = snacks.Sum(r => r.SnackPrice);
…
}

Related

Efficient Way to Update a lot of Rows from C#

I have a program where I open a SqlConnection, load up a list of objects, modify a value on each object, then update the rows in the SQL Server database. Because the modification requires string parsing I wasn't able to do with with purely T-SQL.
Right now I am looping through the list of objects, and running a SQL update in each iteration. This seems inefficient and I'm wondering if there is a more efficient way to do it using LINQ
The list is called UsageRecords. The value I'm updating is MthlyConsumption.
Here is my code:
foreach (var item in UsageRecords)
{
string UpdateQuery = #"UPDATE tbl810CTImport
SET MthlyConsumption = " + item.MthlyConsumption +
"WHERE ID = " + item.Id;
SqlCommand update = new SqlCommand(UpdateQuery, sourceConnection);
update.ExecuteNonQuery();
}
Try this instead:
string UpdateQuery = #"UPDATE tbl810CTImport SET MthlyConsumption = #consumption WHERE ID = #itemId";
var update = new SqlCommand(UpdateQuery, sourceConnection);
update.Parameters.Add("#consumption", SqlDbType.Int); // Specify the correct types here
update.Parameters.Add("#itemId", SqlDbType.Int); // Specify the correct types here
foreach (var item in UsageRecords)
{
update.Parameters[0].Value = item.MthlyConsumption;
update.Parameters[1].Value = item.Id;
update.ExecuteNonQuery();
}
It should be faster because:
You don't have to create the command each time.
You don't create a new string each time (concatenation)
The query is not parsed at every iteration (Just changes the parameters values).
And it will cache the execution plan. (Thanks to #JohnCarpenter from the comment)
You can either use
SqlDataAdapter - See How to perform batch update in Sql through C# code
or what I have previously done was one of the following:
Tear down the ID's in question, and re-bulkinsert
or
Bulk Insert the ID + new value into a staging table, and update the table on SQL server:
update u
set u.MthlyConsumption = s.MthlyConsumption
from tbl810CTImport u
inner join staging s on
u.id = s.id
In a situation like this, where you can't write a single update statement to cover all your bases, it's a good idea to batch up your statements and run more than one at a time.
var commandSB = new StringBuilder();
int batchCount = 0;
using (var updateCommand = sourceConnection.CreateCommand())
{
foreach (var item in UsageRecords)
{
commandSB.AppendFormat(#"
UPDATE tbl810CTImport
SET MthlyConsumption = #MthlyConsumption{0}
WHERE ID = #ID{0}",
batchCount
);
updateCommand.Parameters.AddWithValue(
"#MthlyConsumption" + batchCount,
item.MthlyConsumption
);
updateCommand.Parameters.AddWithValue(
"#ID" + batchCount,
item.MthlyConsumption
);
if (batchCount == 500) {
updateCommand.CommandText = commandSB.ToString();
updateCommand.ExecuteNonQuery();
commandSB.Clear();
updateCommand.Parameters.Clear();
batchCount = 0;
}
else {
batchCount++;
}
}
if (batchCount != 0) {
updateCommand.ExecuteNonQuery();
}
}
It should be as simple as this . . .
private void button1_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection("Server=YourServerName;Database=YourDataBaseName;Trusted_Connection=True");
try
{
//cmd new SqlCommand( "UPDATE Stocks
//SET Name = #Name, City = #cit Where FirstName = #fn and LastName = #add";
cmd = new SqlCommand("Update Stocks set Ask=#Ask, Bid=#Bid, PreviousClose=#PreviousClose, CurrentOpen=#CurrentOpen Where Name=#Name", con);
cmd.Parameters.AddWithValue("#Name", textBox1.Text);
cmd.Parameters.AddWithValue("#Ask", textBox2.Text);
cmd.Parameters.AddWithValue("#Bid", textBox3.Text);
cmd.Parameters.AddWithValue("#PreviousClose", textBox4.Text);
cmd.Parameters.AddWithValue("#CurrentOpen", textBox5.Text);
con.Open();
int a = cmd.ExecuteNonQuery();
if (a > 0)
{
MessageBox.Show("Data Updated");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
con.Close();
}
}
Change the code to suit your needs.

How can more than one user Insert data to the SQL database at the same time without mix the data on the database.

Well I develop an application under the asp and C#. The idea of this application is to multiple users log in to the app and inserts data to the SQL database server 2008 sp2 at the same time.
An user create a subject and then inserting description about this subject , a single user can insert any description data without any problem but when more than one users inserting data then the data does not saved or the data mixed on the databases.
For Example if user one inserting for the Subject 1 the data: User1 User1.1 and User two inserting for the Subject 2 the data: User2 User2.1 then the user’s one data saved on the subject of users2.
Note that the databases are on Multiuser Mode....
Could you please help me with this problem with examples? Thank you in advanced …
This Is My C# Code :
public void dataInsert()
{
try
{
SqlConnection con = new System.Data.SqlClient.SqlConnection();
con.ConnectionString = ConfigurationManager.ConnectionStrings["beta"].ConnectionString;
// storeMulti();
for (int i = 0; i < howManyFields(); i++)
{
TextBox txt = Theat.FindControl("TextBox" + (i + 1)) as TextBox;
HtmlTableCell cell = Theat.FindControl("Td" + (i + 1)) as HtmlTableCell;
CheckBox cb = Theat.FindControl("CheckBox" + (i + 1)) as CheckBox;
TextBox tTitle = Theat.FindControl("tbTitle" + i) as TextBox;
TextBox tDscr = Theat.FindControl("tbDscr" + i) as TextBox;
DropDownList dConf = Theat.FindControl("ddConf" + i) as DropDownList;
DropDownList dType = Theat.FindControl("ddType" + i) as DropDownList;
HtmlInputFile inFile = Theat.FindControl("tbFile" + i) as HtmlInputFile;
if (txt.Text.Trim().ToString() != string.Empty)
{
//ShowMessageBox("Updated");
DataSet dsPres = findPresDatasetByLang(Convert.ToInt32(Application["langID"]));
for (int z = 0; z < dsPres.Tables[0].Rows.Count; z++)
{
System.Data.SqlClient.SqlCommand cmd;
cmd = new System.Data.SqlClient.SqlCommand();
cmd.Connection = con;
cmd.CommandText = "INSERT INTO [I_SUBJECT_FIELD] VALUES (#p1,#p2,#p3,#p4,#p5)";
cmd.Parameters.AddWithValue("#p1", Convert.ToInt32(Application["subID"].ToString()));
cmd.Parameters.AddWithValue("#p2", Convert.ToInt32(dsPres.Tables[0].Rows[z]["ID"].ToString()));
cmd.Parameters.AddWithValue("#p3", Convert.ToInt32(Application["langID"].ToString()));
cmd.Parameters.AddWithValue("#p4", findFieldTypeID(cell.InnerHtml));
cmd.Parameters.AddWithValue("#p5", txt.Text);
cmd.Connection.Open();
int rowsAffected = cmd.ExecuteNonQuery();
cmd.Connection.Close();
}
}
txt.Text = string.Empty;
cb.Checked = false;
}
}
catch (SqlException sql)
{
ShowMessageBox(sql.Message);
}
catch (Exception exe)
{
ShowMessageBox(exe.Message);
}
}
Look at where you're getting parameter values for the query:
cmd.Parameters.AddWithValue("#p1", Convert.ToInt32(Application["subID"].ToString()));
cmd.Parameters.AddWithValue("#p3", Convert.ToInt32(Application["langID"].ToString()));
You're getting them from the Application value collection. This is not thread safe. All concurrent users of the application share the same Application value collection. So when one user updates a value in that collection, other users will use that value.
The values being inserted to the database should be supplied to this function, not referenced in the global Application collection. So the function signature might look more like this:
public void dataInsert(int subjectId, int languageId)
{
// implementation
}
And the query parameters would come from those values:
cmd.Parameters.AddWithValue("#p1", subjectId);
cmd.Parameters.AddWithValue("#p3", languageId);
That way whenever a user invokes this functionality, the code invoking it would simply provide those values instead of storing them in a global collection.
In general, avoid global values. Especially in multi-threaded applications with multiple concurrent users.

Unable to get dropdown selected value

I have a gridview that is populated with a dropdown list on each row. The DDL is populated with a number from 1 - how ever many rows there are.
I have a save button to save the value of each DDL for each row using a simple loop but the problem is that the dropdown never returns the selected value, it seems to return something like the row index of the gridview + 1.
This is an example of how I select the values in the rows:
3
7
4
6
5
2
1
When I do the same, this is how they will save:
2
3
4
5
6
7
1
This is my code from the save, just to let you know I have tried selectedvalue, selectedindex, selecteditem and text, they all do the same.
//save the data
foreach (GridViewRow r in gvOrder.Rows)
{
if (r.RowType == DataControlRowType.DataRow)
{
//get the details from the grid
Label lblED = r.FindControl("lblED") as Label;
DropDownList ddl = r.FindControl("ddlNewO") as DropDownList;
TextBox txt = r.FindControl("txtNewT") as TextBox;
Label ID = r.FindControl("lblID") as Label;
int Order = Convert.ToInt16(ddl.SelectedValue);
string Text = "";
if (txt.Text != "")
{
Text = txt.Text;
}
//save each row to the db
try
{
SqlConnection dbConnection = new SqlConnection();
dbConnection.ConnectionString = GetConnection.GetConnectionString();
SqlCommand dbCommand = new SqlCommand("PL_UserColumns_Save", dbConnection);
dbCommand.CommandType = CommandType.StoredProcedure;
dbCommand.Parameters.Add("#PageID", SqlDbType.Int).Value = Convert.ToInt16(constPageID);
dbCommand.Parameters.Add("#UserID", SqlDbType.Int).Value = Convert.ToInt16(strUserID);
dbCommand.Parameters.Add("#Order", SqlDbType.Int).Value = Convert.ToInt16(Order);
dbCommand.Parameters.Add("#ColumnID", SqlDbType.Int).Value = Convert.ToInt16(ID.Text);
dbCommand.Parameters.Add("#ED", SqlDbType.Int).Value = Convert.ToInt16(lblED.Text);
dbCommand.Parameters.Add("#Text", SqlDbType.NVarChar).Value = "'" + Text + "'";
dbConnection.Open();
dbCommand.ExecuteNonQuery();
dbConnection.Close();
}
catch (SqlException ex)
{
ExceptionHandling.SQLException(ex, constPageID, constIsSiteSpecific);
}
}
}
to get the DropDownList selected value you have to use this:
ddl.SelectedItem.Value
You can check Request.Form to get the posted values. Assuming that your drop-down names will increase with each row e.g. ddlNew0, ddlNew1, etc., then you can do this
var value = Request.Form["ddlNew" + rowIndex];
I would do it in two possible ways:
Add a bit of JavaScript to populate a hidden field and then grab and separate the values in the back-end
Add auto-postback on dropdowns and an onClick method. Also pass the item id as argument. Then do the saving bit in the onclick method.
I would prefer the second one.

Passing Column name as string in mysql Insert query

this is what i am trying to do after receiving string from the serial port. i get whitespace between the data so i put two loops to eliminate them. i want to recieve data spanning multiple columns and a single row for every single run of do while loop.. Thanks in Advance
string text = sp.readline();
for (int i = 0; i < text.Length; )
{
p = text[i].ToString();
if (p != " ")
{
do
{
x += text[i].ToString();
s = text[i].ToString();
i++;
} while (s != " ");
try
{
string col = "column" + no.ToString();
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "INSERT INTO testdata("+col+")VALUES(?data)";
cmd.Parameters.Add("?data", MySqlDbType.VarChar).Value = x;
cmd.ExecuteNonQuery();
x = "";
p = "";
no++;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
} i++;
}
Sorry to say, you cannot, in any dialect of SQL, provide a table or column name as a bindable variable (or parameter). If it's working for MySQL that's terrific, but if you change over to any other DBMS make and model, it will suddenly stop working.
Also, INSERT means create a new row. You'll either need to insert all the column values at once in a single INSERT statement, or figure out how to INSERT one row and then UPDATE it for each new column value.
if you want to insert a single row having multiple column, then for loop is not required
following is for three columns
int no = 2;
cmd.CommandText = "INSERT INTO testdata(?col1,?col2,?col3)VALUES(?data1,?data2,?data3)";
cmd.Parameters.Add("?col1", MySqlDbType.String).Value = col1;
cmd.Parameters.Add("?col2", MySqlDbType.String).Value = col2;
cmd.Parameters.Add("?col3", MySqlDbType.String).Value = col3;
cmd.Parameters.Add("?data1", MySqlDbType.VarChar).Value = x1;
cmd.Parameters.Add("?data2", MySqlDbType.VarChar).Value = x2;
cmd.Parameters.Add("?data3", MySqlDbType.VarChar).Value = x3;
cmd.ExecuteNonQuery();

Efficient way to retrieve multiple values from the same column

I want to set some buttons text (content) with values I retrieve from a mysql.
till now I always did it like this:
if (rdr.Read())
{
Item1.Visibility = Visibility.Visible;
Item1txt.Text = rdr.GetString("item_name");
}
if (rdr.Read())
{
Item2.Visibility = Visibility.Visible;
Item2txt.Text = rdr.GetString("item_name");
}
if (rdr.Read())
{
Item3.Visibility = Visibility.Visible;
Item3txt.Text = rdr.GetString("item_name");
}
This way works fine because I retrieve the right values in each button, but it makes the readability horrible..
When I started this project I had zero knowledge of C# so I tried some things like:
while (rdr.Read())
{
Item4.Visibility = Visibility.Visible;
Item4txt.Text = rdr.GetString("item_name");
Item5.Visibility = Visibility.Visible;
Item5txt.Text = rdr.GetString("item_name");
}
But this gave me the same value retrieved from my database in my buttons..
example:
button 1: test1 | button 2: test1 | button 3: test1.. etc..
what i need:
button 1: test1 | button2: test2 | button 3: test3.. etc..
Now my knowledge of C# is getting better every day so I want to learn some new things.
Right now I'm trying to use the foreach loop but I have a feeling I'm missing something:
using (MySqlConnection conn = new MySqlConnection(_CS))
{
conn.Open();
string cmd = "SELECT * FROM ordertopos LIMIT 10";
MySqlCommand custid = new MySqlCommand(cmd, conn);
using (MySqlDataReader rdr = custid.ExecuteReader())
{
System.Data.DataTable dt = new System.Data.DataTable();
dt.Load(rdr);
foreach (System.Data.DataRow row in dt.Rows)
{
Orderl1.Text = row["customerid"].ToString();
}
}
}
Essentially I want to know how I can set the content of my buttons, retrieved from mysql, in a more efficient en easier to maintain code..
I'm fairly new with foreach, so please be specific.
i would recommend to do it in several step's for a better reusability
Step 1
List<string> myItems;
using (MySqlConnection conn = new MySqlConnection(_CS))
{
conn.Open();
string cmd = "SELECT * FROM ordertopos LIMIT 10";
MySqlCommand custid = new MySqlCommand(cmd, conn);
using (MySqlDataReader rdr = custid.ExecuteReader())
{
myItems= new List<string>();
while (rdr.Read())
{
myItems.Add(rdr.GetString("item_name"));
}
}
}
Step 2
modified Olivier Jacot-Descombe version
for(int i =0; i< myItems.count; i++) {
Button btn = FindChild<Button>(this, "Item" + i); //"this" will be the control which contains your button's
TextBlock tb = FindChild<TextBlock>(btn, "Item" + i + "txt");
btn.Visibility = Visibility.Visible;
tb.Text =myItems[i];
i++;
}
If you want to scale your problem in order to use loops than you need to have a List or an Array that contains objects for which you want to set values of properties. In your particular case, put your Orders in a List<Order> and then you could use something like this:
int count = 0;
foreach (System.Data.DataRow row in dt.Rows)
{
if(count<orders.Count)
orders[count++].Text = row["customerid"].ToString();
}
You need to traverse through your Items to set respective values. As DJ Kraze suggested, you are just overwriting the same control. And it will have the last accessed value (as it won't be overwritten once loop has ended).
You you just need to have somehow reference to your target controls, or if you are creating controls on the fly, than you can simply pass reference of newly created control every time you access a row from database.
You can access controls by their name instead of their member variable by using the FindChild method found here: WPF ways to find controls.
int i = 1;
while (rdr.Read()) {
Button btn = FindChild<Button>(this, "Item" + i);
TextBlock tb = FindChild<TextBlock>(btn, "Item" + i + "txt");
btn.Visibility = Visibility.Visible;
tb.Text = rdr.GetString("item_name");
i++;
}
This enables you to loop through them by using a counter (i).

Categories