Database update only works SOME of the time [duplicate] - c#

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Data not writing out to Database
I'm trying to update a bit field in my database on a checkbox's checkedchanged event. When it gets checked, it sends a 1. When it is unchecked, it sends a 0. Now I'm not sure why, but these changes only get saved SOME of the time. Would this have anything to do with the "!IsPostBack" ?
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
using (SqlConnection connection = new SqlConnection(connectionString.ToString()))
{
connection.Open();
dataAdapter = new SqlDataAdapter("SELECT * FROM SecureOrders", connection);
dataSet = new DataSet();
dataAdapter.Fill(dataSet, "SecureOrders");
DataView source = new DataView(dataSet.Tables[0]);
DefaultGrid.DataSource = source;
DefaultGrid.DataBind();
connection.Close();
}
}
}
protected void CheckBoxProcess_CheckedChanged(object sender, EventArgs e)
{
bool update;
string checkedString = "UPDATE SecureOrders SET processed = 1 WHERE fName LIKE '%" + DefaultGrid.SelectedRow.Cells[2].Text + "%' AND lName LIKE '% " + DefaultGrid.SelectedRow.Cells[3].Text + "%'";
string uncheckedString = "UPDATE SecureOrders SET processed = 0 WHERE fName LIKE '%" + DefaultGrid.SelectedRow.Cells[2].Text + "%' AND lName LIKE '% " + DefaultGrid.SelectedRow.Cells[3].Text + "%'";
CheckBox cb = (CheckBox)sender;
GridViewRow gvr = (GridViewRow)cb.Parent.Parent;
DefaultGrid.SelectedIndex = gvr.RowIndex;
update = Convert.ToBoolean(DefaultGrid.SelectedValue);
orderByString = orderByList.SelectedItem.Value;
fieldString = searchTextBox.Text;
connectionString = rootWebConfig.ConnectionStrings.ConnectionStrings["secureodb"];
using (SqlConnection connection = new SqlConnection(connectionString.ToString()))
{
connection.Open();
SqlCommand checkedCmd = new SqlCommand(checkedString, connection);
SqlCommand uncheckedCmd = new SqlCommand(uncheckedString, connection);
if (cb.Checked == true)
{
checkedCmd.ExecuteNonQuery();
}
else
{
uncheckedCmd.ExecuteNonQuery();
}
connection.Close();
}

I would recommend setting "EnableViewState" to false on your DataView, and then moving the code from the if (!IsPostBack) section to your page's Pre_Init event. I think that will solve your problem and possibly also help your postbacks go faster.

The !IsPostBack is not your issue, whether or not I like the pattern. The postback event handler is a mess, however.
You have left open a potential SQL injection hole by concatenating strings rather than using parameters. Even if you have to write a sproc and pass in a comma separted list, you are better off than the code you have.
Your decision point is one whether or not a value is set to 1 or 0, yet you branch code on the point of deciding which of two strings to run (one will always be created, consuming cycles, yet NEVER run).
You are creating two command objects, one of which will NEVER be used.
You can determine where things are going wrong by stepping through the code with a wide variety of test cases. Eventually you will hit the one that triggers the issue you have.
A better option would be to separate out the SQL update code into its own routine and send the parameters in. This will reduce the number of moving parts. The only thing that should be in the main event handler (and this can be argued) is the grabbing of the variables from the Grid.
If it were me, I would also consider using the key value for my update statement, lest you have two James Smith's in the database, both which are now processed.
As for candidates why it updates some times and not others? Could very well be that you are ending up with the wrong selected row for some reason. As I don't have a copy of your code to see all the permeatations, I can only guess.

Related

How can I make a DropDownList from the database?

When I choose any option from the dropdown list and then insert it into the database, the first option is chosen automatically, even if I choose the second or third option; only the first option is inserted each time.
Order.aspx.cs
protected void selectList()
{
conn = new SqlConnection(connstr);
conn.Open();
sql = "SELECT * FROM Product";
SqlCommand comm = new SqlCommand(sql, conn);
adap = new SqlDataAdapter(comm);
DataSet ds = new DataSet();
adap.Fill(ds);
ProductID.DataTextField = ds.Tables[0].Columns["Name"].ToString();
ProductID.DataValueField = ds.Tables[0].Columns["Id"].ToString();
ProductID.DataSource = ds.Tables[0];
ProductID.DataBind();
}
protected void Page_Load(object sender, EventArgs e)
{
bindGrid();
selectList();
}
protected void btnAdd_Click(object sender, EventArgs e)
{
selectList();
sql = "INSERT INTO [Order] (CustomerID,ProductID ,EmployeeID,Quantity,Date) VALUES ('" + CustomerID.Text + "','" + ProductID.SelectedValue + "','" + EmployeeID.Text + "','" + Quantity.Text + "','" + Date.Text + "')";
conn = new SqlConnection(connstr);
conn.Open();
comm = new SqlCommand(sql, conn);
comm.ExecuteNonQuery();
conn.Close();
bindGrid();
ScriptManager.RegisterStartupScript(Page, Page.GetType(),
"myPrompt", "alert('Successfully Updated!');", true);
}
Order.aspx
Product ID:
<asp:DropDownList ID="ProductID" runat="server" CssClass="form-control" ></asp:DropDownList>
Actually, the mistake here is the page load. In 99% of ALL pages, you only want to bind and load up on the first page load. And most if not all asp controls will automatic persist for you. So, your big mistake is this:
protected void Page_Load(object sender, EventArgs e)
{
bindGrid();
selectList();
}
The above has to become this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
bindGrid();
selectList();
}
}
In fact, you really can't build a functional web form page unless you follow the above rule. Remember, any button, any post-back, and the page load event will run again.
So yes, page load is the right place, but you in near 99% of cases need to wrap that code in the all important isPostBack = false code stub.
Once you do above, then your whole page will operate quite nice, quite normal, and in near all cases, you find the controls correct persist their values and settings.
So, no, page load is NOT too soon, and page load is VERY much the correct event and place to load up the data bound controls - but, you only want to do this on the really first page load.
In fact, there is probably 1 question near per day on SO, and their woes and problems can be fixed by following the above simple rule. Failure to note the above isPostBack? You can't really even build a working asp.net page.
Page_Load() is too late to bind your list.
Remember, when using web forms, you start from scratch and have to recreate all of your data items on every single request... even simple button click events*. This is why you call bindGrid() in the Page_Load() method. However, part of this process also involves restoring ViewState, so the button click will know what item was selected. The problem is this ViewState data is restored before the Page_Load() method is called. Therefore the grid is still empty, and the SelectedValue information you need to get from ViewState cannot be set correctly.
You can fix this by moving the code that binds your grid data up to the Init or Pre_Init events.
While I'm here, I need to reiterate my comment about SQL Injection. This is a really big deal... the kind of thing that's too important to do wrong even with learning and proof-of-concept projects. I suggest using Google to learn more about using parameterized queries with C#.
Additionally, it's rare to insert selections directly into an Orders table. Often there's a separate "ShoppingCart" table, using a Session Key for the table's primary key, where the user can build up the cart before completing the order and creating the final Order and OrderLines or OrderDetail records.
* For this reason, it's often worthwhile in web forms to do more of this work on the client browser, in javascript.

Trying to UPDATE the SELECTED row from the database in c#

I'm trying to give my client a way to SELECT a specific row by typing the id which is the auto incremented primary key of the table.
But there's an issue Showing the Selected row to the client using Text Boxes and letting the client UPDATE the row's cells by editing the Text Boxes and pressing another button.
I'd be glad if you guide be how to do this since I haven't got any help from the search results.
Here's the uncompleted code:
private void LookUpBtn_Click(object sender, RoutedEventArgs e)
{
if (UserIDUpdateTB.Text == "")
{
MessageBox.Show("Customer ID is needed.", "Error");
}
else
{
SqlConnection con = new SqlConnection(#"Data Source=DESKTOP-8QAH8VK\SQLDB; Initial Catalog=Restaurant_DB; Integrated Security=True;");
con.Open();
SqlCommand lookforcustomer = new SqlCommand("LookForCustomer", con);
lookforcustomer.CommandType = CommandType.StoredProcedure;
lookforcustomer.Parameters.AddWithValue("userid", UserIDUpdateTB.Text);
//lookforcustomer.ExecuteNonQuery();
SqlDataReader reader = lookforcustomer.ExecuteReader();
reader.Read();
object test = reader.GetValue(1);
MessageBox.Show(test.ToString(), "Error");
var id = (int?)lookforcustomer.ExecuteScalar();
con.Close();
}
}
Here's the user interface:
The client enters the customer ID , presses the look up button and five cells of the row which belongs to the entered ID, appear in five other Text Boxes separately. the client makes whatever changes he/she wants by Changing the Text Boxes' text and pressing the "Update Info" button.
I'd be thankful if you help.
This is not a sql/database question, its a question regarding your UI. I'm assuming its WPF, although WinForms properties were pretty similar too.
Each button should have a separate _Click event right? So in the UpdateInfo_click, you can either send a full UPDATE statement to SQL, or detect changes between a model in memory and what is in each TextBox.Text and only update those which change (or best yet in the where clause say WHERE Name = {old value})
Also on each of your buttons you can set a Command= property, which is needed if this were part of a repeater or similar, and you can have the same handler inspect what command it was called with to determine the action needing to be taken
So I think if I understand right, basically what you want to do is pull a customers information when the lookup button is clicked, and update the customers information when the submit button is clicked. Possibly you want to also be able to add new records?
Some of the data readers are a little different, I've used Odbc and Npgsql. But I will try and show the basics of what you want below.
I like to have a separate class that deals with the Database connection, I use a method similar to this to run queries.
private SqlDataReader Query(string query)
{
SqlCommand command = null;
SqlDataReader result_reader = null;
try
{
//conn.Open();
command = new SqlCommand(query_to_perform, database_connection); //database_connection is the same as the as your "con" variable
result_reader = command.ExecuteReader();
this.successful_query = true;
this.error_message = "";
//conn.Close();
}
catch (SqlException ex)
{
this.successful_query = false;
this.error_message = ex.Message;
//destroy the connection on a failure
database_connection = new SqlConnection();
throw;
}
return result_reader;
}
Next we basically need to fill the text boxes from a select statement where the customer id is equal to the customer id on the table
private void LookUpBtn_Click(object sender, RoutedEventArgs e)
{
SqlDataReader reader = ConnectionClass.Query("SELECT * WHERE customer_id = '" + customerIdTextbox.Text + "';")
if (reader.Read())
{
//reader[0] probably is CustomerId
NameTextbox.Text = reader[1].ToString();
LastNameTextbox.Text = reader[2].ToString();
PhoneNumberTextbox.Text = reader[3].ToString();
CellphoneNumberTextbox.Text = reader[4].ToString();
AddressTextbox.Text = reader[5].ToString();
}
}
Update the customer, I'd suggest disabling the CustomerId box after they pull up an account, or they might change the number and update a different customer with all the information pulled from the first customer,
private void SubmitBtn_Click(object sender, RoutedEventArgs e)
{
if (//!exists)
{
CreateNewCustomer();
}
else
{
ConnectionClass.Query("UPDATE table SET name = '" + NameTextbox.Text + "', lastname = '" + LastNameTextbox.Text + "', phonenumber = '" + PhoneNumberTextbox.Text + "', cellphonenumber = '" + CellphoneNumberTextbox.Text + "', address = '" + AddressTextbox.Text + "' WHERE CustomerID = '" + customerIdTextbox.Text + '";");
}
}
I believe this should give you the very basics of what you are trying to do, you can still do the queries the way you were if you don't want to create a separate class to deal with the db connection. You might also want to look in to preventing SQL Injections, you will need to deal with things like apostrophes in the text, and creating a new customer will just use an insert query.

DataTable not retaining added rows

This webpage should flow as such:
Call is made to populateTeamMembers(), which first checks to see if the datatable has no rows (indicating this is the first time the function is called) in case it queries SQL to see who is an existing team member, load that into the datatable sqlTeamMembers
This function also checks if there are any rows in a second datatable, newTeamMembers, and if so it merges the contents of newTeamMembers into sqlTeamMembers. The first time the page loads, the newTeamMembers datatable should always be empty, so no merge is performed.
Finally, now that sqlTeamMembers should contain everybody we need, it binds it to a gridview for display on the web page.
Once it is displayed, there is a dropdownlist under the grid view, with a list of employees that may be added to the team. If the selected index is changed on the ddl, you get a postback and selectedIndexChanged() gets called.
The logic in selectedIndexChanged() is that it queries the selected individuals information into a datatable, warns the user if they are already assigned to a different team, and proceeds to add the employee to the newTeamMembers datatable.
Finally, the populateTeamMembers() is called again, this time there is data in the newTeamMembers datatable, and the merge is performed, before binding to the gridView again.
Due to the nature of the two datatables sqlTeamMembers and newTeamMembers, I have initialized them at the head of my class so they should be accessible to all functions.
It works so far as adding a single new line to sqlTeamMembers, but if I attempt to follow through with a second addition, it is wiping out the first addition. For some reason, newTeamMembers is not retaining the new rows, it only ever contains the most recent addition that selectedIndexChanged() creates.
How can I make newTeamMembers retain the new rows each time selectedIndexChanged() gets called?
public partial class Personnel_Admin_Teams : System.Web.UI.Page
{
SqlConnection cnn = new SqlConnection(ConfigurationManager.ConnectionStrings["WebServerConnectionString"].ConnectionString);
SqlDataReader myreader = default(SqlDataReader);
SqlCommand cmd = new SqlCommand();
string sqlStr;
DataTable sqlTeamMembers = new DataTable();
DataTable newTeamMembers = new DataTable();
...
protected void populateTeamMembers(string TeamID)
{
if (sqlTeamMembers.Rows.Count == 0)
{
/*** Read Team Members with Matching TeamID from Employees Table ***/
cnn.Open();
sqlStr = "SELECT [ID], [LastName]+', '+[FirstName] AS [Name], ISNULL([TeamRole], '') AS [TeamRole] FROM [Employees] WHERE [TeamID] = " + TeamID + ";";
cmd = new SqlCommand(sqlStr, cnn);
sqlTeamMembers.Load(cmd.ExecuteReader());
cnn.Close();
}
if (newTeamMembers.Rows.Count > 0)
{
sqlTeamMembers.Merge(newTeamMembers);
}
gvTeamMembers.DataSource = sqlTeamMembers;
gvTeamMembers.DataBind();
try
{
for (int i = 0; i < gvTeamMembers.Rows.Count; i++)
{
DropDownList ddlTeamRole = gvTeamMembers.Rows[i].Cells[2].FindControl("ddlTeamRole") as DropDownList;
string txtTeamRole = sqlTeamMembers.Rows[i][2].ToString();
txtTeamRole = txtTeamRole.Replace("'", "''");
ddlTeamRole.SelectedValue = txtTeamRole;
}
}
catch (Exception ex)
{
ScriptManager.RegisterClientScriptBlock(Page, typeof(Page), "class", "alert(\"There was an error: " + ex + "\");", true);
}
}
selectedIndexChanged()
{
cnn.Open();
sqlStr = "SELECT [ID], [LastName]+', '+[FirstName] AS [Name], ISNULL([TeamRole], '') AS [TeamRole], ISNULL([TeamID], '0') FROM [Employees] WHERE [ID] = " + ddlAllEmployees.SelectedValue + ";";
cmd = new SqlCommand(sqlStr, cnn);
DataTable addOneMember = new DataTable();
addOneMember.Load(cmd.ExecuteReader());
cnn.Close();
int empTeamID = Convert.ToInt32(addOneMember.Rows[0][3]);
/*** If DT has not been used yet, establish columns ***/
if (newTeamMembers.Columns.Count == 0)
{
newTeamMembers.Columns.Add("ID", typeof(int));
newTeamMembers.Columns.Add("Name", typeof(string));
newTeamMembers.Columns.Add("TeamRole", typeof(string));
}
/*** Remove TeamID Column before merging data back to primary DataTable, perform merge ***/
addOneMember.Columns.RemoveAt(3);
newTeamMembers.Merge(addOneMember);
if (empTeamID != 0)
{
sqlStr = "SELECT [TeamLocation]+' | '+[Department]+' | '+[Type]+' | '+[Team]+' | '+[Shift] AS [Team] FROM [Teams] WHERE [ID] =" + empTeamID + ";";
cmd = new SqlCommand(sqlStr, cnn);
cnn.Open();
myreader = cmd.ExecuteReader();
myreader.Read();
string existingTeam = myreader[0].ToString();
myreader.Close();
cnn.Close();
ScriptManager.RegisterClientScriptBlock(Page, typeof(Page), "class", "alert(\"Warning: User is currently assigned to another team. Employee will be removed from " + existingTeam + " when you save this page.\");", true);
}
}
There are a few things you need to understand.
Web pages are stateless, which means on it's initial pass and subsequent PostBacks you are responsible for saving and restoring state via some persistence medium.
A lot of Server controls can do this for you, but you are opting to manage all of this via DataTables, so you need to implement data persistence. For Database query results, the SqlDataSource control uses the Application Cache. You should use that as well programmatically:
public partial class Personnel_Admin_Teams : System.Web.UI.Page
{
DataTable sqlTeamMembers = null;
DataTable newTeamMembers = null;
protected void Page_Load( object sender, EventArgs e ) {
if( !Page.IsPostBack ){
if( Application.Cache["sqlTeamMembers"] == null )
Application.Cache["sqlTeamMembers"] = new DataTable();
if( Application.Cache["newTeamMembers"] == null )
Application.Cache["newTeamMembers"] = new DataTable();
}
sqlTeamMembers = (DataTable) Application.Cache["sqlTeamMembers"];
newTeamMembers = (DataTable) Application.Cache["newTeamMembers"];
// NOTE: sqlTeamMembers will be null until you make that call to
// populateTeamMembers() so there is no hard and fast rule
// about binding during PageLoad. Bind every time you need
// to reflect changes.
gvTeamMembers.DataSource = sqlTeamMembers;
gvTeamMembers.DataBind();
}
}
Also, just to verify, DataTables are not live, open connections to the database. Changes to DataTables are not automatically written back to your database. You have to make the appropriate INSERT/UPDATE SQL calls.
I think the problem is the placement of this line:
DataTable newTeamMembers = new DataTable();
Since it's at the beginning of the class, it's run each time the page is loaded, meaning everytime you load the page, including when page is a postback. Postback occurs, for example, when the user clicks a button to add a new team member. The page is then posted back to the server with the user entries included.
The behaviour you're looking for is to initialize a new newTeamMembers the first time the page loads, but not when page is a postback. To solve this, declare newTeamMembers at the top of the class, but only initialize it when the page is not a postback, by checking this in the page load event:
public partial class Personnel_Admin_Teams : System.Web.UI.Page
{
DataTable newTeamMembers;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) //page is loaded for the first time
{
newTeamMembers = new DataTable();
}
}
//rest of the code
}

Extension for Datagridview row to pass the value for SQL Server

I made a datagridview and I need to pass my datagridview data to a SQL Server database. For that I have created all the codes and after a button click I want to pass those value so I made a code following but it is giving me an error that this does not contain a definition for .Text and no extension method for text. It is giving me an error line under .Text so what is the correct extension method for text.
private void button1_Click(object sender, EventArgs e)
{
// Adding Values to Data Base
string mySQL = "INSERT INTO Employee_login VALUES ("
+ Convert.ToInt32(dataGridView1.Rows[RowCount - 1].Cells[0].Text.Trim()) + ", '"
+ dataGridView1.Rows[RowCount - 1].Cells[1].Text.Trim() + "','";
}
You need to be calling the .Value property on your dataGridView1, not the .Text property.
Something like this:
private void button1_Click(object sender, EventArgs e)
{
// Adding Values to Data Base
string mySQL = "INSERT INTO Employee_login VALUES ("
+ Convert.ToInt32(dataGridView1.Rows[RowCount - 1].Cells[0].Value) + ", '"
+ dataGridView1.Rows[RowCount - 1].Cells[1].Value + "','";
}
You didn't mention in your post however, if you actually need to call the .Trim() method; which will not work in this case using .Value.
Try using this to databind. You will not have to manually enter values into the database if so.
SqlConnection conn = new SqlConnection(#"Your connection string");
DataTable dt = new DataTable();
SqlDataAdapter dataAdapter = new SqlDataAdapter("select * from Employee_login", conn);
dataGridView1.DataSource = dt;
dataAdapter.Fill(dt);
On the button click event you can provide something like
dataAdapter.Update(dt)

TreeView - Saving changes to a database table

I have a C# winforms app form with a TreeView on it. Populating the TreeView is done using a stored proc and view (not table) from database.
This works fine as it puts the information into the TreeView as all root nodes. I have drag and drop available to the users for the TreeView so they can move nodes and have a parent/child/grandchild/etc. However, many are needed. This also works fine.
What I am struggling with (more like getting frustrated with) is saving the users' TreeView changes back to the database table. I considered making the change with each move but decided to use a button click event when all is done being moved.
I am providing what code I have so far. Just remember that this is not final look of the click event since button is not named plus a few other cleanups needed as well and the sqlUpdate will actually become a stored proc.
In the sqlUpdate there are 2 parameters listed; #parentid and #industryid. If I was to hard code those with a number (ex: 1 and 4) the update works and makes the changes in the table to the correct industry id. When the TreeView is populated it uses three (3) fields from the table; IndustryID, ParentID, IndustryItem. These go into a datatable and to the TreeView.
What I am getting frustrated over is getting the #parentid and #industryid which is why I am posting here for help. I've done updates, deletes, inserts many times but for some reason this is making draw a blank.
I do realize I need to specify the parameters in the code but I'm thinking that is where the issue is. Here are the 2 lines;
command.Parameters.AddWithValue("#parentid", ?????);
command.Parameters.AddWithValue("#industryid", ?????);
And here is the click handler:
private void button6_Click(object sender, EventArgs e)
{
using (SqlConnection conn = new SqlConnection(connStr))
{
SqlCommand command = new SqlCommand();
{
string sqlUpdate = "UPDATE tblIndustry SET IndustryItemParentID = #parentid WHERE IndustryID = #industryid";
try
{
conn.Open();
command.Connection = conn;
command.CommandType = CommandType.Text;
command.CommandText = sqlUpdate;
command.ExecuteNonQuery();
MessageBox.Show("Done");
}
catch (Exception ex)
{
MessageBox.Show("There is an error." + '\r' + '\n' + '\r' + '\n' + ex.ToString(), "NOTICE", MessageBoxButtons.OK);
}
finally
{
conn.Close();
}
}
}
}
Here's one way to do it:
For #industryid, use an auto-incremented id column in the database. Assign that value to the treeview node tags when you populate the treeview. Then use the value of treeview.parent.tag for #parentid.

Categories

Resources