I've got a little problem with my application.
I have a database editor that hangs up sometimes when I try to update the database file. Not every time, but pretty often, and every time it happens right before any changes are made to the database. I figured it's because of not using multithreading. I've only started learning programming recently though, and I'm kind of lost even after reading through a few multithreading explanations. Could someone explain to me how should I implement it in my specific example?
private void adjustStatsButton_Click(object sender, EventArgs e)
{
ReadWrite.AdjustStats(winnerInput.Text, loserInput.Text);
winnerInput.Text = "";
loserInput.Text = "";
Refresh(leaderboardBox);
}
public class ReadWrite
{
public static void AdjustStats(string winner, string loser)
{
SQLiteConnection dbConnection = new SQLiteConnection("Data Source = Leaderboards.sqlite; Version = 3");
string sql = "SELECT * FROM leaderboard WHERE name='" + winner + "'";
SQLiteCommand command = new SQLiteCommand(sql, dbConnection);
dbConnection.Open();
SQLiteDataReader reader = command.ExecuteReader();
double wrating = Convert.ToDouble(reader["rating"]);
int wmatches = Convert.ToInt32(reader["matches"]);
int wwins = Convert.ToInt32(reader["wins"]);
sql = "SELECT * FROM leaderboard WHERE name='" + loser + "'";
command = new SQLiteCommand(sql, dbConnection);
reader = command.ExecuteReader();
double lrating = Convert.ToDouble(reader["rating"]);
int lmatches = Convert.ToInt32(reader["matches"]);
int lwins = Convert.ToInt32(reader["wins"]);
int llosses = Convert.ToInt32(reader["losses"]);
double RC = (1 - ((wrating - lrating) / 200)) * 8;
if (RC < 0) RC *= -1;
if (RC < 4) RC = 4;
else if (RC > 12) RC = 12;
wmatches++;
wwins++;
lmatches++;
llosses++;
wrating += RC;
if (wrating < 0) wrating = 0;
lrating -= RC;
if (lrating < 0) lrating = 0;
double wwinrate = Convert.ToDouble(wwins) / wmatches;
double lwinrate = Convert.ToDouble(lwins) / lmatches;
sql = "UPDATE leaderboard SET rating=" + wrating + ", matches=" + wmatches + ", wins=" + wwins + ", winrate=" + wwinrate + " WHERE name='" + winner + "'";
command = new SQLiteCommand(sql, dbConnection);
command.ExecuteNonQuery();
sql = "UPDATE leaderboard SET rating=" + lrating + ", matches=" + lmatches + ", losses=" + llosses + ", winrate=" + lwinrate + " WHERE name='" + loser + "'";
command = new SQLiteCommand(sql, dbConnection);
command.ExecuteNonQuery();
dbConnection.Close();
}
}
The problem is that you are running the query in the UI thread. Here's an example for using the BackgroundWorker, heavily inspired from this example, and using Arguments. This way it will be running in a separate thread and it will not lock the GUI.
// Class for passing arguments
public class BqArguments
{
public string Winner {get;set}
public string Loser {get;set}
}
And your implementation:
BackgroundWorker _bw; // You need to initialize this somewhere.
private void adjustStatsButton_Click(object sender, EventArgs e)
{
// Maybe this should be initialized in ctor. But for this example we do it here...
_bw = new BackgroundWorker();
var arguments = new BqArguments
{
Winner = winnerInput.Text,
Loser = loserInput.Text
}
_bw.DoWork += bw_DoWork;
_bw.RunWorkerCompleted += bw_RunWorkerCompleted;
_bw.RunWorkerAsync(arguments);
}
private void bw_DoWork (object sender, DoWorkEventArgs e)
{
// Run your query in the background.
var arguments = e.Argument as BqArguments;
ReadWrite.AdjustStats(arguments.Winner, arguments.Loser);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// All done. Let's update the GUI.
winnerInput.Text = "";
loserInput.Text = "";
Refresh(leaderboardBox);
}
You just need to use async await keywords within your c# code and it will eventually lead Asynchronous calls to the DB I have made changes to your Code :
private async void adjustStatsButton_Click(object sender, EventArgs e)
{
await ReadWrite.AdjustStats(winnerInput.Text, loserInput.Text);
winnerInput.Text = "";
loserInput.Text = "";
Refresh(leaderboardBox);
}
public class ReadWrite
{
public static Task AdjustStats(string winner, string loser)
{
return Task.Run(() =>
{
SQLiteConnection dbConnection = new SQLiteConnection("Data Source = Leaderboards.sqlite; Version = 3");
string sql = "SELECT * FROM leaderboard WHERE name='" + winner + "'";
SQLiteCommand command = new SQLiteCommand(sql, dbConnection);
dbConnection.Open();
SQLiteDataReader reader = await command.ExecuteReaderAsync();
double wrating = Convert.ToDouble(reader["rating"]);
int wmatches = Convert.ToInt32(reader["matches"]);
int wwins = Convert.ToInt32(reader["wins"]);
sql = "SELECT * FROM leaderboard WHERE name='" + loser + "'";
command = new SQLiteCommand(sql, dbConnection);
reader = await command.ExecuteReaderAsync();
double lrating = Convert.ToDouble(reader["rating"]);
int lmatches = Convert.ToInt32(reader["matches"]);
int lwins = Convert.ToInt32(reader["wins"]);
int llosses = Convert.ToInt32(reader["losses"]);
double RC = (1 - ((wrating - lrating) / 200)) * 8;
if (RC < 0) RC *= -1;
if (RC < 4) RC = 4;
else if (RC > 12) RC = 12;
wmatches++;
wwins++;
lmatches++;
llosses++;
wrating += RC;
if (wrating < 0) wrating = 0;
lrating -= RC;
if (lrating < 0) lrating = 0;
double wwinrate = Convert.ToDouble(wwins) / wmatches;
double lwinrate = Convert.ToDouble(lwins) / lmatches;
sql = "UPDATE leaderboard SET rating=" + wrating + ", matches=" + wmatches + ", wins=" + wwins + ", winrate=" + wwinrate + " WHERE name='" + winner + "'";
command = new SQLiteCommand(sql, dbConnection);
await command.ExecuteNonQueryAsync();
sql = "UPDATE leaderboard SET rating=" + lrating + ", matches=" + lmatches + ", losses=" + llosses + ", winrate=" + lwinrate + " WHERE name='" + loser + "'";
command = new SQLiteCommand(sql, dbConnection);
await command.ExecuteNonQueryAsync();
dbConnection.Close();
}
});
}
The core of your problem is that you're calling blocking (synchronous) database calls on the UI thread. This blocks your UI thread while it's waiting for the database, instead of keeping it nicely responsive.
In the general case, since these are I/O-based operations, you should be able to make them naturally asynchronous, as per Lakhtey's answer. However, SQLite does not support actual asynchronous operations. :(
So, in this case, your best bet is to just wrap up the database work into a background thread. Note that using Task.Run is far superior to BackgroundWorker:
private async void adjustStatsButton_Click(object sender, EventArgs e)
{
await Task.Run(() => ReadWrite.AdjustStats(winnerInput.Text, loserInput.Text));
winnerInput.Text = "";
loserInput.Text = "";
Refresh(leaderboardBox);
}
Related
I'm new here but I need some help. I need to update a SQL Server database from C# with Windows Forms, but I'm having problems. I looked it up but still can't find the right answer. I need to do insert and update by pressing a button for changing or filling the database from the datagridview. I've created a separate function for both I am using this code;
private void InsertPositionen()
{
string qry = "";
SqlCommand insert = new SqlCommand(qry, con);
try
{
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
qry = "INSERT INTO BelegePositionen (BelID, BelPosId, Artikelnummer, Menge, Preis) VALUES( " + dataGridView1.Rows[i].Cells["BelID"] + ", "
+ dataGridView1.Rows[i].Cells["BelPosId"] + ", "
+ dataGridView1.Rows[i].Cells["Artikelnummer"] + ", "
+ dataGridView1.Rows[i].Cells["Menge"] + ", "
+ dataGridView1.Rows[i].Cells["Preis"];
}
insert.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void UpdatePositionen()
{
string updt = "";
SqlCommand update = new SqlCommand(updt, con);
try
{
for (int i = 0; i < dataGridView1.Rows.Count -1; i++)
{
updt = "UPDATE BelegePositionen SET BelID = "
+ dataGridView1.Rows[i].Cells["BelID"] +
", BelPosID = "
+ dataGridView1.Rows[i].Cells["BelPosID"] +
", Atrikelnummer = "
+ dataGridView1.Rows[i].Cells["Artikelnummer"] +
", Menge = "
+ dataGridView1.Rows[i].Cells["Menge"] +
", Preis = "
+ dataGridView1.Rows[i].Cells["Preis"];
}
update.ExecuteNonQuery();
con.Close();
MessageBox.Show("Done!");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
You should really NOT do your SQL stuff like this!! This leaves your code wide open for SQL injection vulnerabilities! Stop that - right now!
Instead - use parametrized queries - like this:
private void InsertPositionen()
{
string qry = "INSERT INTO BelegePositionen (BelID, BelPosId, Artikelnummer, Menge, Preis) " +
"VALUES(#BelId, #BelPosId, #ArtNr, #Menge, #Preis);";
SqlCommand insert = new SqlCommand(qry, con);
// define the parameters
insert.Parameters.Add("#BelId", SqlDbType.Int);
insert.Parameters.Add("#BelPosId", SqlDbType.Int);
insert.Parameters.Add("#ArtNr", SqlDbType.Int); // maybe this is a string?
insert.Parameters.Add("#Menge", SqlDbType.Int);
insert.Parameters.Add("#Preis", SqlDbType.Decimal, 20, 4);
try
{
// in the loop, only *set* the parameter's values
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
insert.Parameters["#BelId"].Value = 1;
insert.Parameters["#BelPosId"].Value = 2;
insert.Parameters["#ArtNr"].Value = 3;
insert.Parameters["#Menge"].Value = 4;
insert.Parameters["#Preis"].Value = 99.95;
insert.ExecuteNonQuery();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Your Question is quite vague as you state you are having problems, but not quite sure what problems you are having. It will help if you can describe what problems you are having.
In addition to what #marc_c said about sql injection, I can't see how you manage your connection to the database.
From the code it looks like you could run into a situation where you are leaving connection strings open, or not opening them at all.
using the using(...) { } will close the connections when you are done with it.
private void InsertPositionen()
{
//using the using statement you will insure that the connection is closed and resources released
using (SqlConnection connection = new SqlConnection(Properties.Settings.Default.db))
{
string cmd = "INSERT INTO BelegePositionen (BelID, BelPosId, Artikelnummer, Menge, Preis) " +
"VALUES(#BelId, #BelPosId, #ArtNr, #Menge, #Preis);";
//using the using statement will ensure any reasources are released when exiting the code block
using (SqlCommand insert = new SqlCommand(cmd, connection))
{
// define the parameters
insert.Parameters.Add("#BelId", SqlDbType.Int);
insert.Parameters.Add("#BelPosId", SqlDbType.Int);
insert.Parameters.Add("#ArtNr", SqlDbType.Int); // maybe this is a string?
insert.Parameters.Add("#Menge", SqlDbType.Int);
insert.Parameters.Add("#Preis", SqlDbType.Decimal, 20, "4");
try
{
//open the connection
insert.Connection.Open();
// in the loop, only *set* the parameter's values
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
insert.Parameters["#BelId"].Value = dataGridView1.Rows[i].Cells["BelID"];
insert.Parameters["#BelPosId"].Value = dataGridView1.Rows[i].Cells["BelPosId"];
insert.Parameters["#ArtNr"].Value = dataGridView1.Rows[i].Cells["Artikelnummer"];
insert.Parameters["#Menge"].Value = dataGridView1.Rows[i].Cells["Menge"];
insert.Parameters["#Preis"].Value = dataGridView1.Rows[i].Cells["Preis"];
insert.ExecuteNonQuery();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
MessageBox.Show("Done!");
}
}
}
}
Okay, so i'm trying to get 18 "prices" from my database in SQL, then setting them in a local array. So far i have this logic in the data retrieval:
private void dbPrices()
{
string myConnectionString;
myConnectionString = "server=127.0.0.1;uid=root;" +
"pwd=;database=phvpos";
try
{
conn = new MySql.Data.MySqlClient.MySqlConnection();
conn.ConnectionString = myConnectionString;
conn.Open();
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
MessageBox.Show(ex.Message);
}
for (int i = 1; i < 19; i++)
{
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "SELECT price from products where id = '" + i + "'";
MySqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
pr[i] = reader.ToString();
prods[i] = int.Parse(pr[i]);
}
}
}
And this logic in getting the total amount:
private void btnTotal_Click(object sender, EventArgs e)
{
dbPrices();
itemcost[0] = Convert.ToInt32(txtRice.Text) * prods[0];
itemcost[1] = Convert.ToInt32(txtAdobo.Text) * prods[1];
itemcost[2] = Convert.ToInt32(txtIgado.Text) * prods[2];
itemcost[3] = Convert.ToInt32(txtSisig.Text) * prods[3];
...
itemcost[18] = itemcost[0] + itemcost[1] + itemcost[2] + itemcost[3] + itemcost[4] + itemcost[5]
+ itemcost[6] + itemcost[7] + itemcost[8] + itemcost[9] + itemcost[10]
+ itemcost[11] + itemcost[12] + itemcost[13] + itemcost[14] + itemcost[15]
+ itemcost[16] + itemcost[17];
int totalPrice = itemcost[18];
lblTotal.Text = Convert.ToString(totalPrice);
}
this line in dbPrices() spits out a 'input string was not in a correct format' error:
while (reader.Read())
{
pr[i] = reader.ToString();
prods[i] = int.Parse(pr[i]);
}
I have also tried:
while (reader.Read())
{
prods[i] = Convert.toInt32(reader.ToString());
}
But also spits the same error. Is there anything that i'm doing wrong?
Try this. I find that when using 'reader' that even when your query only grabs one value you still need to specify what value your looking for.
prods[i] = int.Parse(reader["price"]);
and if price is nullable in the database use a ternary operator
prods[i] = reader["price"] == DBNull.Value ? 0 : int.parse(reader["price"]);
The value from your result needs to convert to int and your for loop block might cause you serious problem in the future, you might want to have a projection query and then assign the values of the result.
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandText = $"SELECT price FROM products WHERE id BETWEEN 1 AND 18"; //use projection. if you have a dynamic product id set, you can use IN in query.
MySqlDataReader reader = cmd.ExecuteReader();
var counter = 0; //counter
while (reader.Read())
{
prods[counter] = Convert.ToInt32(reader["price"]); //convert the result first then assign it to the item.
counter++;
}
I have 4 radio buttons. Each one of them corresponds to one type (columns in the DB) of the input temperature that I wanna use.
What do I already have:
If I choose one radio button + press Load - it plots the graph. If I choose again this button (or any of the other Radiobuttons) it plots in a sequence of the original graph.
What do I need help with:
I would like that each time that I press the button "Load" the Line would be "added" to the existing graph. In other words, I may have 4 different lines in the same graph, each one representing on radio button that I selected and pressed "Load."
Code:
private void BtnLoadDataToGraph_Click(object sender, EventArgs e)
{
string column_to_use = "";
double column_percentage_XX = 0;
double column_percentage_XX = 0;
string ReceiveNameFile = CboxReceiveNameFile.Text;
if (RadioButtonStartXXTemp.Checked)
column_to_use = "START_XX_TEMP";
else if (RadioButtonStartXXTemp.Checked)
column_to_use = "START_XX_TEMP";
if (RadioButtonAvgTemp.Checked)
column_to_use = "(START_XX_TEMP + START_XX_TEMP)/2";
else
{
column_percentage_XX = Int32.Parse(TextBoxXXPercentage.Text);
column_percentage_XX = (Convert.ToDouble(column_percentage_XX) / 100);
column_percentage_XX = Int32.Parse(TextBoxXXPercentage.Text);
column_percentage_XX = (Convert.ToDouble(column_percentage_XX) / 100);
column_to_use = "(START_XX_TEMP*" + column_percentage_XX + ")+(START_XX_TEMP*" + column_percentage_XX + ")";
}
SqlConnection conDatabase = new SqlConnection("XXXXXX");
SqlCommand cmdDatabase = new SqlCommand("select " + column_to_use + " AS temp, SUBSTRING (header.TIME,CHARINDEX(' ',header.TIME,1),len(header.TIME)) as time,CONVERT(datetime,header.TIME,101) as new_time, REVERSE(SUBSTRING(REVERSE(fnames.PATH_NAME),0,CHARINDEX('\\\',REVERSE(fnames.PATH_NAME)))) as folder_name from TBL_DATA_TYPE_RO_HEADER header,TBL_FILE_NAMES fnames,TBL_PROGRAM program where program.PK_ID_TBL_PROGRAM = fnames.FK_ID_TBL_PROGRAM and fnames.PK_ID_TBL_FILE_NAMES = header.FK_ID_TBL_FILE_NAMES and REVERSE(SUBSTRING(REVERSE(fnames.PATH_NAME),0,CHARINDEX('\\\',REVERSE(fnames.PATH_NAME))))='" + ReceiveNameFile + "' order by new_time", conDatabase);
SqlDataReader myReader;
try
{
conDatabase.Open();
myReader = cmdDatabase.ExecuteReader();
while (myReader.Read())
{
this.ChartTempVsTime.Series["TimeVsTemp"].Points.AddXY(myReader["time"].ToString(), myReader["temp"].ToString());
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Any ideas of how can I do this? I was thinking in maybe use "Points.Aggregate" instead of "Points.AddXY". But I don't think that it is the right path especially because each radiobutton should has a different color line.
Any help or tips are welcome!
Just create a new series in the chart and add the new points into it
this.ChartTempVsTime.Series["TimeVsTemp"].Points.AddXY(myReader["time"].ToString(), myReader["temp"].ToString());
This line is always plotting your data on the same series. You need to make a new series each time you press the load button. Something like:
private void BtnLoadDataToGraph_Click(object sender, EventArgs e)
{
try
{
string column_to_use = "";
double column_percentage_XX = 0;
double column_percentage_XX = 0;
string ReceiveNameFile = CboxReceiveNameFile.Text;
if (RadioButtonStartXXTemp.Checked)
column_to_use = "START_XX_TEMP";
else if (RadioButtonStartXXTemp.Checked)
column_to_use = "START_XX_TEMP";
if (RadioButtonAvgTemp.Checked)
column_to_use = "(START_XX_TEMP + START_XX_TEMP)/2";
else
{
column_percentage_XX = Int32.Parse(TextBoxXXPercentage.Text);
column_percentage_XX = (Convert.ToDouble(column_percentage_XX) / 100);
column_percentage_XX = Int32.Parse(TextBoxXXPercentage.Text);
column_percentage_XX = (Convert.ToDouble(column_percentage_XX) / 100);
column_to_use = "(START_XX_TEMP*" + column_percentage_XX + ")+(START_XX_TEMP*" + column_percentage_XX + ")";
}
SqlConnection conDatabase = new SqlConnection("XXXXXX");
SqlCommand cmdDatabase = new SqlCommand("select " + column_to_use + " AS temp, SUBSTRING (header.TIME,CHARINDEX(' ',header.TIME,1),len(header.TIME)) as time,CONVERT(datetime,header.TIME,101) as new_time, REVERSE(SUBSTRING(REVERSE(fnames.PATH_NAME),0,CHARINDEX('\\\',REVERSE(fnames.PATH_NAME)))) as folder_name from TBL_DATA_TYPE_RO_HEADER header,TBL_FILE_NAMES fnames,TBL_PROGRAM program where program.PK_ID_TBL_PROGRAM = fnames.FK_ID_TBL_PROGRAM and fnames.PK_ID_TBL_FILE_NAMES = header.FK_ID_TBL_FILE_NAMES and REVERSE(SUBSTRING(REVERSE(fnames.PATH_NAME),0,CHARINDEX('\\\',REVERSE(fnames.PATH_NAME))))='" + ReceiveNameFile + "' order by new_time", conDatabase);
SqlDataReader myReader;
conDatabase.Open();
myReader = cmdDatabase.ExecuteReader();
Series s = new Series();
while (myReader.Read())
{
s.Points.AddXY(myReader["time"].ToString(), myReader["temp"].ToString());
}
chart1.Series.Add(s);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
I have a method (AddRowToSQLTable()) that is passed a row, it then creates a row array containing that row and then passes it to DataTable.update().
This works fine. The SQL Server is updated and the next time I run (debug) the program the new data is present.
Another method (AddStock()) uses a similar structure but does not work.
The difference being that I am passing a data table in Update() not a Row[] however no exception is thrown just nothing is updated/added on the server.
Can someone please explain what exactly is going on and why these differ? I would like to know this even if the solution is to use a different structure to achieve what I want so I know for the future and have a better understanding.
To Note:
I have tested to ensure the DataTable being passed contains records. The DataTable is of the same format as the target SQL Server table.
I have also tried SqlBulkCopy and encounter the same problem.
Many thanks!
private DataTable StockAdditions;
public void PrepareStockQuantitiesForAdding()
{
if (StockAdditions != null)
{
StockAdditions.Clear();
}
StockAdditions = StockQuantitiesTable.Clone();
int Count = 0;
DataRow RowToAdd = StockQuantitiesTable.NewRow();
DataView AreaIDs = new DataView(AreaTable);
DataView Conditions = new DataView(ConditionsTable);
int AreaID;
string ConditionCode;
foreach (DataRow Row in SessionSKUScanned.Rows)
{
if (Row["Serial Number"].ToString() == "")
{
Conditions.RowFilter = "([Name] = '" + Row["Condition"] + "')";
ConditionCode = Conditions[0][2].ToString();
AreaIDs.RowFilter = "([StorageAreaName] = '" + Row["Area"] + "')";
AreaID = Convert.ToInt32(AreaIDs[0][0]);
DataView StockQuantities = new DataView(StockQuantitiesTable);
StockQuantities.RowFilter = "([QuantityID] = '" + AreaID + "-" + Row["SKU"] + "-" + ConditionCode + "')";
if (StockQuantities.Count > 0)
{
StockQuantities[0][3] = Convert.ToInt32(StockQuantities[0][3]) + 1;
RowToAdd["QuantityID"] = AreaID + "-" + Row["SKU"] + "-" + ConditionCode;
RowToAdd["SKU"] = Row["SKU"];
RowToAdd["AreaID"] = AreaID;
RowToAdd["Quantity"] = StockQuantities[0][3] = Convert.ToInt32(StockQuantities[0][3]) + 1;
RowToAdd["Condition"] = Row["Condition"];
}
else
{
RowToAdd["QuantityID"] = AreaID + "-" + Row["SKU"] + "-" + ConditionCode;
RowToAdd["SKU"] = Row["SKU"];
RowToAdd["AreaID"] = AreaID;
RowToAdd["Quantity"] = 1;
RowToAdd["Condition"] = Row["Condition"];
}
Count++;
}
else
{
RowToAdd["QuantityID"] = Row["Serial Number"];
RowToAdd["SKU"] = Row["SKU"];
AreaIDs.RowFilter = "([StorageAreaName] = '" + Row["Area"] + "')";
RowToAdd["AreaID"] = Convert.ToInt32(AreaIDs[0][0]);
RowToAdd["Quantity"] = 1;
RowToAdd["Condition"] = Row["Condition"];
Count++;
}
StockQuantitiesTable.ImportRow(RowToAdd);
MessageBox.Show(RowToAdd[0].ToString());
StockAdditions.ImportRow(RowToAdd);
}
}
private void AddRowToSQLTable(DataRow Row, string SQLTableName)
{
DataRow[] Rows = new DataRow[1];
Rows[0] = Row;
SqlConnection SQLConn = new SqlConnection(ConfigurationManager.ConnectionStrings["eCommStock.Properties.Settings.Demo_SiteConnectionString"].ConnectionString);
SQLConn.Open();
SqlDataAdapter daUpdateTable = new SqlDataAdapter("Select * From " + SQLTableName, SQLConn);
SqlCommandBuilder SQLcmdBuilder = new SqlCommandBuilder(daUpdateTable);
daUpdateTable.Update(Rows);
SQLConn.Close();
}
public void AddStock()
{
SqlConnection SQLConn = new SqlConnection(ConfigurationManager.ConnectionStrings["eCommStock.Properties.Settings.Demo_SiteConnectionString"].ConnectionString);
SQLConn.Open();
SqlDataAdapter daUpdateTable = new SqlDataAdapter("Select * From StockQuantities", SQLConn);
SqlCommandBuilder SQLcmdBuilder = new SqlCommandBuilder(daUpdateTable);
daUpdateTable.Update(StockAdditions);
SQLConn.Close();
}
It doesn't work because RowToAdd.RowState is Detached. I don't like it, but the following is a quick and dirty solution, before importing the row to StockAdditions, make sure the row is in Added state, later you can "detach" the row again.
StockQuantitiesTable.Rows.Add(RowToAdd);
StockAdditions.ImportRow(RowToAdd);
StockQuantitiesTable.Rows.Remove(RowToAdd);
i face the following problem::
i wanna to escape the following character ' single quote:
it works when making this test through :the built in method Replace("'","''");
as this code below :(just a test) it works
protected void btn_insert_Click(object sender, EventArgs e)
{
lbl.Text = string.Empty;
SqlConnection mycon = new SqlConnection(Constr);`
SqlCommand mycommand = new SqlCommand("INSERT INTO details VALUES('" + txt.Text.Replace("'", "''") + "','" + txt.Text.Replace("'", "''")+ "')", mycon);
mycon.Open();
int affectedRows = 0;
affectedRows = mycommand.ExecuteNonQuery();
mycon.Close();
}
but i wanna to generalize my solution to work all over the application through my Insert method in the data access layer:
public static int InsertEntity(string tblName, Dictionary<string, string> dtParams)
{
int Result = -1;
DBConnection DAL_Helper = new DBConnection("");
string[] field_names = new string[dtParams.Count];
dtParams.Keys.CopyTo(field_names, 0);
string[] field_values = new string[dtParams.Count];
dtParams.Values.CopyTo(field_values, 0);
for (int i = 0; i < field_values.Length; i++)
{
field_values[i].Replace("'", "''");
}
string insertCmd = "INSERT INTO " + tblName + " (" + string.Join(",", field_names) + ") values ('" + string.Join("','", field_values) + "')";
Result = DAL_Helper.Execute_NonQuery(insertCmd);
return Result;
}
this not escaping the ' single quote charecter,although i use Replace("'","''");
what is the problem ,,how to fix this problem?
I strongly recommend you use Command Parameters using SqlCommand.Parameters collection instead of your approach.
Problem is here :
for (int i = 0; i < field_values.Length; i++)
{
field_values[i].Replace("'", "''");
}
Replace it with :
for (int i = 0; i < field_values.Length; i++)
{
field_values[i] = field_values[i].Replace("'", "''");
}
Building on decyclone's answer. CommandParameters are the way to go here, you are just re-inventing it with your own code.
I have found a very nice clear example here for supplying params to a SQL statement.
http://dotnetperls.com/sqlparameter
using (SqlCommand command = new SqlCommand("SELECT * FROM Dogs1 WHERE Name LIKE #Name", connection))
{
string dogName = "Mc'Dougal";
//
// Add new SqlParameter to the command.
//
command.Parameters.Add(new SqlParameter("Name", dogName));
//
// Read in the SELECT results.
//
SqlDataReader reader = command.ExecuteReader();
}