Update multiple checkboxlist values to SQL DB asp.net C# - c#

I am updating a row in SQL DB using the code below. The loop works and it updates the row but the problem is that each time it goes through the loop, it only updates one value and the other values are overwritten. So at the end, it has updated but instead of multiple values being inputted to the table for the respective Project ID, it only puts one value for the respective Project ID. I am not receiving any errors for this. Any help is much appreciated.
for (int i = 0; i < cbAvailableEntities.Items.Count - 1; i++)
{
SqlConnection connection = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand("UpdateProjectEntity", connection);
using (connection)
{
connection.Open();
using (cmd)
{
if (cbAvailableEntities.Items[i].Selected)
{
cmd.CommandType = CommandType.StoredProcedure;
//the following is the Project ID for the row being updated.
SqlParameter paramPID = new SqlParameter("#ProjectID", nr.ProjectID);
cmd.Parameters.Add(paramPID);
nr.Entities = cbAvailableEntities.Items[i].Value;
cmd.Parameters.AddWithValue("#CorpID", nr.Entities);
cmd.ExecuteNonQuery();
}
}
}
}
Here is the SQL query for the Stored Procedure "UpdateProjectEntity"
ALTER PROCEDURE [dbo].[UpdateProjectEntity]
#ProjectID int,
#CorpID int
AS
BEGIN
UPDATE [dbo].[ProjectEntity]
SET
[CorpID] = #CorpID
WHERE
ProjectID = #ProjectID
END
Here are screenshots of inputs and results when I run the program.
These are the checkboxes I am saving to the DB
This is the result after I have saved to the DB
I changed the date to show that everything else works in this program.

I can see you save your enity INT, maybe you should save it as a Comma Separated String.
So instead of save 1, you can save 1,2,3
Of course you will have to add some logic before the save building and concat the string. And also need to do some parsing when you read from db doing the split by ,
The other aproach is creating a relation table to indicate with are the options selected.
But this is also have problem when you remove a selection and add new ones.
ProjectID CorpID
1 1
1 2
1 3

The way I resolved this without making any changes to the DB is I used a DELETE Statement to delete the rows with the ProjectID and then I used an insert stored procedure that I have used before. It was a lot faster than creating another table among all that is already in place. So the code looks like this
for (int i = 0; i < cbAvailableEntities.Items.Count - 1; i++) {
SqlConnection connection = new SqlConnection(connString);
SqlCommand com = new SqlCommand("InsertProjectEntity", connection);
SqlCommand dcm = new SqlCommand();
using(connection) {
//First time going through the loop, i = 0 is true.
if (i == 0) {
connection.Open();
using(com) {
//This will remove anything in the DB related to the ProjectID being edited.
dcm.Connection = connection;
dcm.CommandText = "DELETE FROM [dbo].[ProjectEntity] WHERE ProjectID = " + _pID;
dcm.ExecuteNonQuery();
//This will insert all items checked in the checkboxlist but will not insert the unchecked.
if (cbAvailableEntities.Items[i].Selected) {
com.CommandType = CommandType.StoredProcedure;
SqlParameter paramPID = new SqlParameter("#ProjectID", nr.ProjectID);
com.Parameters.Add(paramPID);
nr.Entities = cbAvailableEntities.Items[i].Value;
com.Parameters.AddWithValue("#CorpID", nr.Entities);
com.ExecuteNonQuery();
}
}
} else {
connection.Open();
using(com) {
//This will insert all items checked in the checkboxlist but will not insert the unchecked.
if (cbAvailableEntities.Items[i].Selected) {
com.CommandType = CommandType.StoredProcedure;
SqlParameter paramPID = new SqlParameter("#ProjectID", nr.ProjectID);
com.Parameters.Add(paramPID);
nr.Entities = cbAvailableEntities.Items[i].Value;
com.Parameters.AddWithValue("#CorpID", nr.Entities);
com.ExecuteNonQuery();
}
}
}
}

Related

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.

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();
}
}

Listing a number twice in a list box when pulling data from a database using the and key word in the sql statment in C#

Sorry for the large heading, I don't know what is going on with my code. I am pulling all serial numbers for a given work order number and status code and populating a list box with the results. My issue is, my code is pulling the number but listing it twice in the list box control. I am using a postgres database. Here is my code.
private void Get_Serial_Numbers()
{
NpgsqlConnection conn = Connection.getConnection();
try
{
conn.Open();
NpgsqlCommand cmd = new NpgsqlCommand("select product_serial_number from master_product where product_wo_number = :WorkOrder and status = :Status;", conn);
cmd.Parameters.Add(new NpgsqlParameter("WorkOrder", IdStorage.WorkOrderNumber));
cmd.Parameters.Add(new NpgsqlParameter("Status", 128));
NpgsqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
object serialNumber = dr["product_serial_number"];
lstProductsInWO.Items.Add(serialNumber.ToString());
}
if (lstProductsInWO.Items.Count == 0)
{
lstProductsInWO.Items.Add("No Data");
lblSerialInWO.Text = "Products in WO 0";
}
else
{
ProductTotal = lstProductsInWO.Items.Count;
lblSerialInWO.Text = "Products in WO " + ProductTotal.ToString();
}
dr.Close();
cmd.Dispose();
}
There are two possilities:
You accidently call the method Get_Serial_Numbers() twice in some event handlers - check it by debugging to make sure that the code runs only once.
You get the items twice from the table. Verify what the query returns (under the debugger) or running it manually against the database.

Two insert queries with linked fields

I have the following two MySQL tables:
questions:
question_id (PK, AI), module_id (FK), author_id (FK), approved, question, correct_answer_id (FK)
answers:
answer_id (PK, AI), question_id (FK), answer
I want to be able to insert a new row in the 'questions' table and multiple rows in the 'answers' tables.
The new rows in the 'answers' table should have the same 'question_id' as the newly generated 'question_id' value in the 'questions' row. Also, the 'correct_answer_id' field in the 'questions' table should equal the 'answer_id' of the first row inserted in the 'answers' table.
Is there a more efficiently way to do this than the following steps?:
insert values (module_id, author_id, approved, question) in
'questions'
get last 'question_id' in 'questions'
insert values (question_id, answer) in 'answers'
update value (correct_answer_id) in 'questions'
code:
string connStr = ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString;
MySqlConnection conn = new MySqlConnection(connStr);
string queryUpdateQuestions = "INSERT INTO questions (module_id, author_id, approved, question) VALUES (#module_id, #author_id, #approved, #question)";
MySqlCommand cmdUpdateQuestions = new MySqlCommand(queryUpdateQuestions, conn);
cmdUpdateQuestions.Parameters.Add("#module_id", MySqlDbType.VarChar);
cmdUpdateQuestions.Parameters["#module_id"].Value = ddlModules.SelectedValue.ToString();
cmdUpdateQuestions.Parameters.Add("#author_id", MySqlDbType.VarChar);
cmdUpdateQuestions.Parameters["#author_id"].Value = Session["UserID"].ToString();
cmdUpdateQuestions.Parameters.Add("#approved", MySqlDbType.VarChar);
cmdUpdateQuestions.Parameters["#approved"].Value = 'N';
cmdUpdateQuestions.Parameters.Add("#question", MySqlDbType.VarChar);
cmdUpdateQuestions.Parameters["#question"].Value = txtQuestion.Text;
try
{
conn.Open();
cmdUpdateQuestions.ExecuteNonQuery();
}
catch
{
lblError.Text="Unable to add question.";
}
finally
{
conn.Close();
}
//????? = get last question_id in 'questions'
int a = Convert.ToInt32(ddlNoOfAnswers.SelectedValue.ToString());
for (int b=1; b <= a; b++)
{
string queryUpdateAnswers = "INSERT INTO answers (question_id, answer) VALUES (#question_id, #answer)";
MySqlCommand cmdUpdateAnswers = new MySqlCommand(queryUpdateAnswers, conn);
cmdUpdateAnswers.Parameters.Add("#answer", MySqlDbType.VarChar);
cmdUpdateAnswers.Parameters["#answer"].Value = ((TextBox)this.FindControl("txtAnswer" + b)).Text;
cmdUpdateAnswers.Parameters.Add("#question_id", MySqlDbType.VarChar);
cmdUpdateAnswers.Parameters["#question_id"].Value = ?????;
try
{
conn.Open();
cmdUpdateAnswers.ExecuteNonQuery();
}
catch
{
lblError.Text="Unable to add answer.";
}
finally
{
conn.Close();
}
}
//update 'correct_answer_id' in 'questions'
Some simplification is possible. First of all you need to enclose all of your commands inside a transaction because this is the classical case where the records inserted are in strictly relationships and it doesn't make sense to have some partially completed set of records.
using(MySqlConnection conn = new MySqlConnection(connStr))
{
conn.Open();
using(MySqlTransaction tr = conn.BeginTransaction())
{
...
// MySqlCommand code goes here
...
tr.Commit();
}
}
Now, you could change your insert question sql to add a second statement that returns the last id inserted
string queryUpdateQuestions = #"INSERT INTO questions (.....);
SELECT LAST_INSERT_ID()";
using(MySqlCommand cmdUpdateQuestions = new MySqlCommand(queryUpdateQuestions, conn, tr))
{
// build the parameters for the question record
......
// Instead of ExecuteNonQuery, run ExecuteScalar to get back the result of the last SELECT
int lastQuestionID = Convert.ToInt32(cmdUpdateQuestions.ExecuteScalar());
..
}
Notice how, at the MySqlCommand constructor, is passed the reference to the current transaction. This is required to work with an connection that has a transaction opened.
Things are a bit more complex for the second part. The same trick to add a second sql statement could be applied also to the loop that insert the answers, but you need to loop backward if the first question is the correct one
string queryUpdateAnswers = #"INSERT INTO answers (question_id, answer)
VALUES (#question_id, #answer);
SELECT LAST_INSERT_ID()";
using(MySqlCommand cmdUpdateAnswers = new MySqlCommand(queryUpdateAnswers, conn, tr))
{
// next move the loop inside the using and prepare the parameter before looping to
// to avoid unnecessary rebuild of the parameters and the command
cmdUpdateAnswers.Parameters.Add("#answer", MySqlDbType.VarChar);
cmdUpdateAnswers.Parameters.Add("#question_id", MySqlDbType.Int32);
int lastAnswerID = 0;
// Loop backward so the last answer inserted is the 'correct' one and we could get its ID
for (int b=a; b >= 1; b--)
{
cmdUpdateAnswers.Parameters["#answer"].Value = ((TextBox)this.FindControl("txtAnswer" + b)).Text;
cmdUpdateAnswers.Parameters["#question_id"].Value = lastQuestionID;
lastAnswerID = Convert.ToInt32(cmdUpdateAnswers.ExecuteScalar());
}
....
}
Now you could run the last command that update the question with the lastAnswerID
(A last note, I suppose that the fields question_id and answer_id are of type numeric, not varchar, this requires that the parameters for these fields will be an Int32 not a varchar)
Yes, the approach you outline is the most efficient. You will need to retrieve the value assigned to the AUTO_INCREMENT column of each row INSERTED. But be careful how you retrieve that value.
insert a row into 'questions' table
retrieve last_insert_id value assigned to AUTO_INCREMENT column
insert row to 'answers' table, using retrieved value for 'question_id' column
retrieve last_insert_id value immediately following insert of "correct answer" row
update row in 'questions' to set 'correct_answer_id' column
MySQL provides the LAST_INSERT_ID() function. That's the mechanism that MySQL provides to retrieve the value assigned to an AUTO_INCREMENT column, following the successful execution of an INSERT statement. (With singleton inserts, it's very straightforward; it just has to be called immediately following the insert.)
Ref: http://dev.mysql.com/doc/refman/5.5/en/information-functions.html#function_last-insert-id
A lot of client libraries provide a builtin function to do this, so it's not necessary to prepare and execute a separate SELECT statement. (For example, with PHP, PDO provides lastInsertId, mysqli provides $insertid. It's likely that the C# connector for MySQL has a similar function.)

SQL query only pulling data from one table

So basically, i have 2 tables, Tenants and Owners. My stored procedure is inner joining to display the select results in textboxes. But unfortunately, the query is only pulling from the owner table and not both. I get an error when trying to display from the tenant table that the column does not exist.
#ID varchar(4)
SELECT owner_table.ownerID AS ownID, tenant_table.tenantID, owner_table.apt_num AS aptNum, owner_table.first_name AS own_first, owner_table.last_name AS own_last,
owner_table.address AS own_address, owner_table.city AS own_city, owner_table.state AS own_state, owner_table.zip AS own_zip,
owner_table.phone AS own_phone, owner_table.phone_2 AS own_phone2, owner_table.notes AS own_notes, owner_table.last_update AS own_lastUpdate,
tenant_table.ownerID AS ten_ownID, tenant_table.last_name, tenant_table.keyID, tenant_table.storage, tenant_table.phone, tenant_table.phone_2, tenant_table.notes,
tenant_table.complaints, tenant_table.lease_end, tenant_table.last_update, tenant_table.apt_status
FROM owner_table INNER JOIN
tenant_table ON owner_table.ownerID = #ID AND tenant_table.tenantID = #ID
RETURN
Code trying to get a result from the procedure.
private void LoadInfo(string ID)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand Command = new SqlCommand();
Command.Connection = connection;
Command.CommandText = "MoreInfoData";
Command.CommandType = CommandType.StoredProcedure;
Command.Parameters.Add(new SqlParameter("#ID", SqlDbType.VarChar, 4)).Value = ID;
table = new DataTable();
SqlDataAdapter MyAdapter = new SqlDataAdapter();
MyAdapter.SelectCommand = Command;
MyAdapter.Fill(table);
if (table.Rows.Count > 0)
{
ten_name.Text = table.Rows[0]["last_name"].ToString();
}
}
Modify to use the column alias:
ten_name.Text = table.Rows[0]["own_last"].ToString();
You changed your field name as own_last, so of course it doesnt exist. owner_table.last_name AS own_last. So you should change your code into
ten_name.Text = table.Rows[0]["own_last"].ToString();
Have you tried adding an alias for tenant_table.last_name?:
SELECT .... tenant_table.last_name AS tenant_last_name
and then:
ten_name.Text = table.Rows[0]["tenant_last_name"].ToString();
Can you run this and see what comes in the output, should list all your column names for the datatable you are retrieving.
if (table.Rows.Count > 0)
{
foreach (System.Data.DataColumn col in table.Columns)
{
System.Diagnostics.Debug.WriteLine(col.ColumnName);
}
}
All in all, somehow whatever i saved to my query at the time wouldnt save for some reason. This ended up being the problem after all. Thanks for all the help.

Categories

Resources