I have an email application that will be called upon to deliver to the UI the number of new messages for a given user on each page load.
I have a few variations of things I am testing on the DB level but all are abstracted by the stored proc call.
I'm trying to slam the DB to see what the breaking point would be by creating a simple multi-threaded app to call the proc multiple times and show a timestamp of the results in a grid. Then I just scroll down the grid to see the duration from the first result to the last to determine how many results i can serve per second.
I think this because SQL should be able to handle hundreds of these calls per second but the time stamps show only 4 per second. This seems way too long to select a value based on an ID within a clustered index column.
In a nutshell, I have a table such as this userId, newMsgCount with a clustered index on userId. SQL should be able to server hundreds of these responses per second.
I think the laggard is my .NET app.
How can I make this a good test to achieve the test results based on SQL performance?
I have a textbox that takes a number of DB calls and a button to invoke the test, then a grid to show results.
private void btnSend_Click(object sender, EventArgs e)
{
int count = Convert.ToInt32(txtThreadCount.Text);
dtStatus = new DataTable();
dtStatus.Columns.Add(new DataColumn("Thread No."));
//dtStatus.Columns.Add("User Id");
dtStatus.Columns.Add("Count");
dtStatus.Columns.Add("time");
for (int i = 0; i < count; i++)
{
ThreadPool.QueueUserWorkItem(GetMessageCount, i);
}
MessageBox.Show("Results retrieved successfully. \n Please see the result tab.");
grdEmail.DataSource = dtStatus;
grdEmail.Refresh();
}
private static void GetMessageCount(object threadContext)
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = new SqlConnection();
cmd.Connection.ConnectionString = "Data Source=server1;Initial Catalog=emails;Persist Security Info=True;User ID=IS_User;Password=M1";
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "sel_Message_New_Count";
cmd.Parameters.AddWithValue("#UserId", 4);
SqlDataAdapter da = new SqlDataAdapter();
DataSet ds = new DataSet();
da.SelectCommand = cmd;
da.Fill(ds);
if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
{
DataRow dr = dtStatus.NewRow();
dr[0] = dtStatus.Rows.Count + 1;
//dr[1] = 1;
dr[1] = ds.Tables[0].Rows[0][0];
dr[2] = DateTime.Now.ToString("hh:mm.ss:ffff");
dtStatus.Rows.Add(dr);
}
}
You may want to try changing the number of threads the ThreadPool is using. See the ThreadPool.SetMinThreads method.
You have massive memory leak issues. You need a using statement on SQLCommand, SQLConnection, SQLDataAdapter.
Don't use a DataSet, use a DataTable and dispose of it in a using statement.
Are you sure that DataTable.NewRow() is a thread safe construct whereby you can just slam it full of rows?
Why are you executing that code in a button click event? The button click event should spawn a background thread, which in turn executes the code you currently have in the button click event. The UI should then display a status bar indicating the progess of that background thread...
At the heart of your problem is the fact that you're not waiting for all those threadpool threads you spawn to finish. You start them, then immediately display a MessageBox.
You need to wait for each thread to finish.
How do you do that? Here's one solution - or - you could try using Parallel.For if you're using .NET 4 and the Task Parallel Library.
Related
I am new to C# and I am using windows forms.
I am building an application and I am facing a strange serious problem when Form loads.
I have 2 forms:
Form1 has a button_Edit and DataGridView
Form2 has a DataGridView1 and DataGridView2
As it is shown in the code and the screen shot, in Form1 when I select a row in DataGridView then click on Button_Edit the Order number and DateTime values in DataGridView on Form1 are passed to Form2 and then Form2 opens up.
Now in Form2 Load event there is a SQL query which takes Order number and DateTime to bring the relevant order details and then fill DataGridView1 and DataGridView2 in Form2.
In Form1:
Form2 frm2 = new Form2();
private void button_Edit_Click(object sender, EventArgs e)
{
frm2._ Order_Number= Convert.ToInt32(dataGridView1.SelectedRows[0].Cells[0].Value);
frm2._ Date_Time= Convert.ToDateTime(dataGridView1.SelectedRows[0].Cells[4].Value);
frm2.ShowDialog();
}
In Form2:
SqlConnection MyConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString);
SqlCommand MyCommand = new SqlCommand();
DataTable DataTable = new DataTable();
SqlDataAdapter Sql_Data_Adapter = new SqlDataAdapter();
int Order_Number;
DateTime Date_Time;
int i;
double Sum;
int RowIndex;
public int _ Order_Number
{
set { Order_Number = value; }
}
public DateTime _ Date_Time
{
set { Date_Time = value; }
}
private void Form2_Load(object sender, EventArgs e)
{
DataTable.Rows.Clear();
DataTable.Columns.Clear();
MyConnection.Open();
MyCommand.CommandText = "SELECT * FROM Customer_Order_Details WHERE Order_Number = #OrderNumber and Date_Time = #DateTime ";
MyCommand.Connection = MyConnection;
MyCommand.Parameters.Add("#OrderNumber", SqlDbType.Int).Value = Order_Number;
MyCommand.Parameters.Add("#DateTime", SqlDbType.DateTime).Value = Date_Time;
Sql_Data_Adapter.SelectCommand = MyCommand;
Sql_Data_Adapter.Fill(DataTable);
MyCommand.Parameters.Clear();
MyConnection.Close();
dataGridView1.Rows.Clear();
dataGridView2.Rows[0].Cells[1].Value = 0;
Sum = 0;
//////////////FILL THE ORDER INTO DATAGRIDVIEW1///////////
RowIndex = DataTable.Rows.Count - 1;
for (i = 0; i <= RowIndex; i++)
{
dataGridView1.Rows.Add(DataTable.Rows[i][2], DataTable.Rows[i][3], DataTable.Rows[i][4]);
// Calculate the total:
Sum = Convert.ToDouble(DataTable.Rows[i][4]) + Sum;
}
dataGridView2.Rows[0].Cells[1].Value = sum;
}
The issue:
This code works fine and as I wanted and DataGridView1 & DataGridView2 in Form2 are filled with the right details and it works fine when Form2 loads.
However, sometimes Form2 freezes after filling both DataGridView1 & DataGridView2 when Form2 loads and I can not do anything until I kill the Application using Task manager.
This issue happens sometimes and it is unpredictable. I really don’t know what is wrong.
I had a look here , here and here but non of those questions related to my issue. Note that I already use try catch and it does not throw anything because the form freezes. This freeze behavior occurs in the release mode I mean after I build EXE file then I install it in a PC and here the issue happens.
Has anyone got any idea why this bad unpredictable behavior occurs?
Is there anything that I should change in my code?
I will be very happy to listen to any new ideas/solutions no matter how small it is, it will be very beneficial.
Thank you
Do SQL work on another thread. Check out Asynchronous call.
BeginInvoke
I suggest using Async versions of Open (connecting to RDBMS) and ExecuteReader (query executing):
con.Open() -> await con.OpenAsync()
q.ExecuteReader() -> await q.ExecuteReaderAsync()
this easy substitution makes UI responsible (it doesn't freeze) while connecting to RDBMS and executing the query.
// do not forget to mark Form2_Load method as async
private async void Form2_Load(object sender, EventArgs e) {
// Do not share the connection - create it and dispose for each call
using (SqlConnection con = new SqlConnection(...)) {
await con.OpenAsync();
string sql =
#"SELECT *
FROM Customer_Order_Details
WHERE Order_Number = #OrderNumber
AND Date_Time = #DateTime";
// do not share command/query as well
using (SqlCommand q = new SqlCommand(sql, con)) {
q.Parameters.Add("#OrderNumber", SqlDbType.Int).Value = Order_Number;
q.Parameters.Add("#DateTime", SqlDbType.DateTime).Value = Date_Time;
dataGridView1.Rows.Clear();
dataGridView2.Rows[0].Cells[1].Value = 0;
Sum = 0;
// We actually don't want any sql adapter:
// all we have to do is to fetach data from cursor and append it to grids
using (var reader = await q.ExecuteReaderAsync()) {
while (reader.Read()) {
dataGridView1.Rows.Add(reader[2], reader[3], reader[4]);
Sum += Convert.ToDouble(reader[4]);
}
}
}
}
}
as said the code snippet you gave us isn't well written. You should use more Try/Catch Statements to prevent crashs and freezes on your programm. This will help you to find bugs even while running the Programm in release. Furthermore you have many options to solve your problem.
1:
Try to start Form2 in a second thread, then only your form2 will freeze until your sql finishes
2:
As mentioned before try to use asyncronous calls to avoid freeze time of handling the sql
3:
There is no direct need of a SQL Database/Connection. You could also use a Collection and create objects defined of your Products (like cola) and them bind them through the database. (If you are interested in i could show you a example)
The best way for you would be:
Add some Try/Catch Statements and get familiar with it, get familiar with Systems.Thread and try to start your form2 in a new thread if this doenst work go to step 2 and add asynchronus calls
In the end i would like to tell you, that namen your Forms "Form1" and "Form2" isn't nice could maybe you like to change it.
I'm using WeifenLuo dockpanel-suit.
I need to show some kind of splash form or loading message before SQL complets loading data.
What I've tried didn't work
public partial class frmPostventa : DockContent
{
private void frmPostventa_Load(object sender, EventArgs e)
{
bool done = false;
ThreadPool.QueueUserWorkItem((x) =>
{
using (var splashForm = new SplashForm())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
});
//Database task heavy load
cargarDatos();
done = true;
}
public void cargarDatos()
{
string strSQL = "exec SAI_ALGO.spPostventa";
SqlDataAdapter dataAdapter = new SqlDataAdapter(strSQL, strCon);
SqlCommandBuilder commandBuilder = new SqlCommandBuilder(dataAdapter);
// Populate a new data table and bind it to the BindingSource.
DataTable table = new DataTable();
table.Locale = System.Globalization.CultureInfo.InvariantCulture;
dataAdapter.Fill(table);
bindingSource1.DataSource = table;
}
}
EDIT: Added CargarDatos()
DoEvents() nearly always leads to a very bad place, and should pretty much always be avoided. The problem with this particular implementation is that your long running process (cargarDatos()) is going to block the UI thread until it completes, so DoEvents() will have no effect; it will not interrupt a method while it is executing (assuming cargarDatos() isn't doing something internally that would yield the thread). Also, creating and interacting with a UI control from some background thread is not valid here.
You should use a BackgroundWorker to perform the long running operation and raise status updates to the UI: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
nice people! I am stuck at one problem here and really need your help. I have a Winforms application, which has a few tabs. It connects to MS SQL Server 2008 DB and shows us a DataGridView on the first tab where we
connect to the DB:
private void button_ConnectDB_Click(object sender, EventArgs e)
{
if (sqlConnection == null || sqlConnection.State != ConnectionState.Open)
sqlConnection = new SqlConnection(connectionString);
sqlConnection.Open();
try
{
dataAdapter = new SqlDataAdapter(queryStringUsers, sqlConnection);
SetConnectDBObjects();
SetConnectDBButtons();
}
catch
{
ShowUnableToConnect();
return;
}
}
and have some info from DB:
private void button_SpecAcc_Click(object sender, EventArgs e)
{
dataGridView1.DataSource = this.usersBindingSource;
LoadData(ref dataAdapter, ref clientDataSet, TAB_USERS);
dataGridView1.ReadOnly = false;
},
where LoadData is actually:
private void LoadData(ref SqlDataAdapter dataAdapter, ref clientDataSet dataset, string table)
{ dataAdapter.Fill(dataset, table); }
I have the dataAdapter connected straight to SQL Server, so its configured automatically (all operations, like INSERT, UPDATE, DELETE also). And everything worked just fine (could connect to DB, make any operations, save it (via Apply button, which uses adapter"s Update method)), until Ive decided to make another tab.
Another tab should get me the info (several rows) from other place (some ext device I have connected with), get it into DataTable and MERGE with other rows from the DB. Did it all on this tab following this tutorial: website.
When I try to MERGE some row I can see in my DB nice and good UPDATE, BUT in my first tab (on the form) I can see TWO different rows (one, as if it wasn"t touched and second - as if it was updated). I supposed it''s something with Refreshing (tried Refreshing of GridView, didn"t help), or some other element"s Refresh (but which one?...), and I still can not find it.
Would appreciate any help... Thanks
Dealt with it through creating temp dataset, editing it (via DataRows) and getting it into a database. Thanks all the watchers :)
I'm an Amateur at C# and I don't understand what's going on here. It's not really a problem, as I can make a quickfix for it, but I still want to know Why.
Disclaimer: I know this is probably not the best way to design this program, but I have been given a very short amount of time to develop it and I'm just trying to ship it by the deadline.
I have a main form in my program. This form calls a second form when 1 of 2 buttons are pressed. These buttons use the same function to open the second form:
private void setupShow(int show)
{
fSetup setup = new fSetup(show);
setup.Show();
setup.FormClosed += new FormClosedEventHandler(setup_FormClosed);
}
When button1 calls that function, it hides the main form and opens the next form. However, when the second button calls that function it keeps the main form open, but still opens the second form.
So what could be causing this?
Button1 has quite a bit more code than button2 and if the functions are needed, I can post them here but it would fill up the majority of the screen.
Edit:
int show;
Is just a variable I use for functions performed in the second form. It has no bearing on anything as far as windows closing.
Button 1: Only calls this function. The function inserts into a database, then gets the inserted ID of that row and passes it to the second form
private void CheckFields()
{
OleDbCommand insertParty;
OleDbDataAdapter partyAdapt = new OleDbDataAdapter();
int nameL = PName.Text.Length;
int newPartyID = 0;
if (nameL > 0)
{
String test = "INSERT INTO Parties (PartyName, BackgroundImg) VALUES (?, ?)";
insertParty = new OleDbCommand(test, Parties);
insertParty.Parameters.AddWithValue("PartyName", PName.Text);
insertParty.Parameters.AddWithValue("BackgroundImg", tBrowse.Text);
Parties.Open();
insertParty.ExecuteNonQuery();
NewPartyForm.ActiveForm.Visible = false;
OleDbCommand selectnewParty;
OleDbDataAdapter newpartyAdaptr = new OleDbDataAdapter();
String selectNew = "SELECT TOP 1 PartyID, PartyName FROM Parties ORDER BY PartyID DESC";
selectnewParty = new OleDbCommand(selectNew, Parties);
OleDbDataReader newReader = selectnewParty.ExecuteReader();
while (newReader.Read())
{
newPartyID = newReader.GetInt32(0);
}
setupShow(newPartyID);
}
else
MessageBox.Show("Please Create a Party Name");
}
Button 2: Cuts out the integer from the string in a combobox and passes it to the second form
private void bLoad_Click(object sender, EventArgs e)
{
Object selectedParty = cLoadP.SelectedItem;
String sP = selectedParty.ToString();
String d1 = " - ";
char[] delim = d1.ToCharArray();
String[] numS = sP.Split(delim);
setupShow(Convert.ToInt32(numS[0]));
}
Like I said, this code is being developed by an amateur and very quickly. This is not the way I would normally do things, but I don't have much time to really think. lol
Second form initial function:
public fSetup(int partyID)
{
InitializeComponent();
pID = partyID;
lpID.Text += " " + pID.ToString();
}
I suspect that the cause of the different behavior is this line in the code for button 1:
NewPartyForm.ActiveForm.Visible = false;
There's no similar line in the code for button 2.
He everybody,
Recently I've started a project that needs a lot of data. So much in fact it cannot be stored in the runtime memory.
I've been looking for solutions to this problem and have chosen for the use of a SQL databases.
I found a tutorial explaining how they work and recreated it myself. It makes sure your physical database and virtual dataset stay in sync by using a dataAdapter to update the database;
(FYI the tutorial can be found here: http://www.homeandlearn.co.uk/csharp/csharp_s12p1.html);
However when expanding a tutorial i came across an error i cant explain namely: DBConcurrencyException.
When i run the program all functionality works, i can add a row and delete a row. But when i try to do both in the same execution (order = add then delete) the error comes up.
It describes that zero of the one expected records were affected by the DeleteCommand.
The following code snippits is what i used:
Initialization:
private void Form1_Load(object sender, EventArgs e)
{
con = new SqlConnection();
ds1 = new DataSet();
con.ConnectionString = "Data Source=.\\SQLEXPRESS;AttachDbFilename=C:\\<Private>\\MyWorkers.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True";
con.Open();
string sql = "SELECT * From tblWorkers";
da = new SqlDataAdapter(sql, con);
da.Fill(ds1, "Workers");
NavigateRecords();
MaxRows = ds1.Tables["Workers"].Rows.Count;
updateIndicator();
con.Close();
//con.Dispose();
}
(Note: I commented out the directory for privacy reasons);
The addition of a row is done as followed:
private void btnSave_Click(object sender, EventArgs e)
{
SqlCommandBuilder cb = new SqlCommandBuilder(da);
DataRow dRow = ds1.Tables["Workers"].NewRow();
dRow[1] = textBox1.Text;
dRow[2] = textBox2.Text;
dRow[3] = textBox3.Text;
ds1.Tables["Workers"].Rows.Add(dRow);
MaxRows = MaxRows + 1;
inc = MaxRows - 1;
da.Update(ds1, "Workers");
MessageBox.Show("Entry Added");
}
When deleting a row this code is executed:
private void btnDelete_Click(object sender, EventArgs e)
{
SqlCommandBuilder cb = new SqlCommandBuilder(da);
ds1.Tables["Workers"].Rows[inc].Delete();
MaxRows--;
inc = 0;
NavigateRecords();
da.Update(ds1, "Workers");
MessageBox.Show("Record Deleted");
}
The NavigateRecord() method and setIndicator() method are not relevant in this question since they are GUI elements.
Thanks in advance everybody!
I came across this question statement while looking for a way to prevent SQL Server from locking an entire record (I want it to lock by field / column value instead) and noticed that no answer had been given, so here ya go if anyone else runs into this problem:
OP didn't actually ask a question, only stated what the problem was; so here's the answer to the question that wasn't asked "How do I prevent Database Concurrency Violations?":
Database Concurrency Violations (in this case) are preventable by making a decision as to which data gets to go in to the database. Most people elect to take the approach that 'last in wins'. So, in your handling of operations using the TableAdapter, put the Update in a Try/Catch and during Catch for DBConcurrencyException just make sure you do a quick Fill, then change whatever you were supposed to be changing, and then do the Update. Works every time.