Why does my C# Winforms program with a timer get bogged down after a day?
I have a program that updates two DataGridView objects every minute with a MySQL query from our internal orders database so we can keep track of older orders and make sure they're being handled.
It works well, but if you leave the program on it starts to get slower and slower and you really notice it after a day or so. Restarting the program fixes this, but I'd really like to know why I can't just leave this running.
Here is the timer code and the function it calls:
private void timer1_Tick(object sender, EventArgs e)
{
UpdateOrderDisplay();
}
private void UpdateOrderDisplay()
{
grdOrderItems.Rows.Clear();
grdHoldOrders.Rows.Clear();
string strsql;
string CustomerName;
string MyConString = "SERVER=**********;" + "DATABASE=***********;" + "UID=************;" + "PASSWORD=***********;";
using (MySqlConnection connection = new MySqlConnection(MyConString))
{
MySqlCommand command = connection.CreateCommand();
MySqlDataReader Reader;
strsql = "select * from orders where ship_reference=0 and OnHold =0 order by order_id asc";
command.CommandText = strsql;
connection.Open(); Reader = command.ExecuteReader();
while (Reader.Read())
{
if (Reader["payment_date"].ToString() != "")
{
if (Reader["custom"].ToString().Contains("~*"))
{
CustomerName = Reader["custom"].ToString().Substring(0, Reader["custom"].ToString().IndexOf("~"));
}
else
{
CustomerName = Reader["shipping_address_name"].ToString();
}
grdOrderItems.Invoke(new MethodInvoker(() => grdOrderItems.Rows.Add(Convert.ToDateTime(Reader["payment_date"].ToString().Substring(0, 21)).ToString(), Reader["txn_id"].ToString(), CustomerName, Reader["mc_gross"].ToString(), Reader["memo"].ToString(), Reader["order_id"].ToString())));
}
}
connection.Close();
strsql = "select * from orders where OnHold =1 order by order_id asc";
command.CommandText = strsql;
connection.Open(); Reader = command.ExecuteReader();
while (Reader.Read())
{
if (Reader["payment_date"].ToString() != "")
{
if (Reader["custom"].ToString().Contains("~*"))
{
CustomerName = Reader["custom"].ToString().Substring(0, Reader["custom"].ToString().IndexOf("~"));
}
else
{
CustomerName = Reader["shipping_address_name"].ToString();
}
grdHoldOrders.Invoke(new MethodInvoker(() => grdHoldOrders.Rows.Add(Convert.ToDateTime(Reader["payment_date"].ToString().Substring(0, 21)).ToString(), Reader["txn_id"].ToString(), CustomerName, Reader["mc_gross"].ToString(), Reader["memo"].ToString(), Reader["order_id"].ToString(),Reader["Hold_Review_Date"].ToString().Substring(0,Reader["Hold_Review_Date"].ToString().IndexOf(" ")),Reader["payer_email"].ToString())));
}
}
connection.Close();
}
}
Also, in case it's relevant, here is some code I use to color code the lines of the DataGridView object so we can easily tell which order is older:
private void grdOrderItems_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
grdOrderItems.RowPrePaint += new System.Windows.Forms.DataGridViewRowPrePaintEventHandler(this.grdOrderItems1_RowPrePaint);
}
private void grdOrderItems1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
if (e.RowIndex <= grdOrderItems.Rows.Count - 1)
{
string StringNow = DateTime.Now.ToString();
string NowTime = StringNow.Substring(StringNow.IndexOf(" ")+1, StringNow.Length- StringNow.IndexOf(" ")-1);
string OrderDateTime = grdOrderItems.Rows[e.RowIndex].Cells[0].Value.ToString().Substring(0, grdOrderItems.Rows[e.RowIndex].Cells[0].Value.ToString().IndexOf(" ")+1) + NowTime;
if ((Convert.ToDateTime(StringNow) - Convert.ToDateTime(OrderDateTime)).Days > 2)
{
grdOrderItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Tomato;
}
else
{
if ((Convert.ToDateTime(StringNow) - Convert.ToDateTime(OrderDateTime)).Days > 1)
{
grdOrderItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Yellow;
}
else
{
if ((Convert.ToDateTime(StringNow) - Convert.ToDateTime(OrderDateTime)).Days > 0)
{
grdOrderItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Tan;
}
}
}
}
}
c# mysql winforms datagridview timer
|
show 10 more comments
I have a program that updates two DataGridView objects every minute with a MySQL query from our internal orders database so we can keep track of older orders and make sure they're being handled.
It works well, but if you leave the program on it starts to get slower and slower and you really notice it after a day or so. Restarting the program fixes this, but I'd really like to know why I can't just leave this running.
Here is the timer code and the function it calls:
private void timer1_Tick(object sender, EventArgs e)
{
UpdateOrderDisplay();
}
private void UpdateOrderDisplay()
{
grdOrderItems.Rows.Clear();
grdHoldOrders.Rows.Clear();
string strsql;
string CustomerName;
string MyConString = "SERVER=**********;" + "DATABASE=***********;" + "UID=************;" + "PASSWORD=***********;";
using (MySqlConnection connection = new MySqlConnection(MyConString))
{
MySqlCommand command = connection.CreateCommand();
MySqlDataReader Reader;
strsql = "select * from orders where ship_reference=0 and OnHold =0 order by order_id asc";
command.CommandText = strsql;
connection.Open(); Reader = command.ExecuteReader();
while (Reader.Read())
{
if (Reader["payment_date"].ToString() != "")
{
if (Reader["custom"].ToString().Contains("~*"))
{
CustomerName = Reader["custom"].ToString().Substring(0, Reader["custom"].ToString().IndexOf("~"));
}
else
{
CustomerName = Reader["shipping_address_name"].ToString();
}
grdOrderItems.Invoke(new MethodInvoker(() => grdOrderItems.Rows.Add(Convert.ToDateTime(Reader["payment_date"].ToString().Substring(0, 21)).ToString(), Reader["txn_id"].ToString(), CustomerName, Reader["mc_gross"].ToString(), Reader["memo"].ToString(), Reader["order_id"].ToString())));
}
}
connection.Close();
strsql = "select * from orders where OnHold =1 order by order_id asc";
command.CommandText = strsql;
connection.Open(); Reader = command.ExecuteReader();
while (Reader.Read())
{
if (Reader["payment_date"].ToString() != "")
{
if (Reader["custom"].ToString().Contains("~*"))
{
CustomerName = Reader["custom"].ToString().Substring(0, Reader["custom"].ToString().IndexOf("~"));
}
else
{
CustomerName = Reader["shipping_address_name"].ToString();
}
grdHoldOrders.Invoke(new MethodInvoker(() => grdHoldOrders.Rows.Add(Convert.ToDateTime(Reader["payment_date"].ToString().Substring(0, 21)).ToString(), Reader["txn_id"].ToString(), CustomerName, Reader["mc_gross"].ToString(), Reader["memo"].ToString(), Reader["order_id"].ToString(),Reader["Hold_Review_Date"].ToString().Substring(0,Reader["Hold_Review_Date"].ToString().IndexOf(" ")),Reader["payer_email"].ToString())));
}
}
connection.Close();
}
}
Also, in case it's relevant, here is some code I use to color code the lines of the DataGridView object so we can easily tell which order is older:
private void grdOrderItems_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
grdOrderItems.RowPrePaint += new System.Windows.Forms.DataGridViewRowPrePaintEventHandler(this.grdOrderItems1_RowPrePaint);
}
private void grdOrderItems1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
if (e.RowIndex <= grdOrderItems.Rows.Count - 1)
{
string StringNow = DateTime.Now.ToString();
string NowTime = StringNow.Substring(StringNow.IndexOf(" ")+1, StringNow.Length- StringNow.IndexOf(" ")-1);
string OrderDateTime = grdOrderItems.Rows[e.RowIndex].Cells[0].Value.ToString().Substring(0, grdOrderItems.Rows[e.RowIndex].Cells[0].Value.ToString().IndexOf(" ")+1) + NowTime;
if ((Convert.ToDateTime(StringNow) - Convert.ToDateTime(OrderDateTime)).Days > 2)
{
grdOrderItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Tomato;
}
else
{
if ((Convert.ToDateTime(StringNow) - Convert.ToDateTime(OrderDateTime)).Days > 1)
{
grdOrderItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Yellow;
}
else
{
if ((Convert.ToDateTime(StringNow) - Convert.ToDateTime(OrderDateTime)).Days > 0)
{
grdOrderItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Tan;
}
}
}
}
}
c# mysql winforms datagridview timer
Dispose thoseCommand
s.
– GSerg
Nov 13 '18 at 18:13
1
Yeah, close the reader explicitly after use (or put it in ausing
), would be my first thing to change.
– 500 - Internal Server Error
Nov 13 '18 at 18:15
3
Is thegrdOrderItems_RowPrePaint
an actual live handler that also fires? If so, you are adding another subscriber togrdOrderItems.RowPrePaint
each time it fires, and after a while eachgrdOrderItems.RowPrePaint
will cause thousands of executions ofthis.grdOrderItems1_RowPrePaint
.
– GSerg
Nov 13 '18 at 18:15
1
@MatheusRocha No it won't. It will continue to exist until collected by the garbage collector, which may not happen in a while. That is precisely the reason whyIDisposable
exists.
– GSerg
Nov 13 '18 at 18:23
1
You don't need to close and re-open the connection in between the two commands.
– Joel Coehoorn
Nov 13 '18 at 18:31
|
show 10 more comments
I have a program that updates two DataGridView objects every minute with a MySQL query from our internal orders database so we can keep track of older orders and make sure they're being handled.
It works well, but if you leave the program on it starts to get slower and slower and you really notice it after a day or so. Restarting the program fixes this, but I'd really like to know why I can't just leave this running.
Here is the timer code and the function it calls:
private void timer1_Tick(object sender, EventArgs e)
{
UpdateOrderDisplay();
}
private void UpdateOrderDisplay()
{
grdOrderItems.Rows.Clear();
grdHoldOrders.Rows.Clear();
string strsql;
string CustomerName;
string MyConString = "SERVER=**********;" + "DATABASE=***********;" + "UID=************;" + "PASSWORD=***********;";
using (MySqlConnection connection = new MySqlConnection(MyConString))
{
MySqlCommand command = connection.CreateCommand();
MySqlDataReader Reader;
strsql = "select * from orders where ship_reference=0 and OnHold =0 order by order_id asc";
command.CommandText = strsql;
connection.Open(); Reader = command.ExecuteReader();
while (Reader.Read())
{
if (Reader["payment_date"].ToString() != "")
{
if (Reader["custom"].ToString().Contains("~*"))
{
CustomerName = Reader["custom"].ToString().Substring(0, Reader["custom"].ToString().IndexOf("~"));
}
else
{
CustomerName = Reader["shipping_address_name"].ToString();
}
grdOrderItems.Invoke(new MethodInvoker(() => grdOrderItems.Rows.Add(Convert.ToDateTime(Reader["payment_date"].ToString().Substring(0, 21)).ToString(), Reader["txn_id"].ToString(), CustomerName, Reader["mc_gross"].ToString(), Reader["memo"].ToString(), Reader["order_id"].ToString())));
}
}
connection.Close();
strsql = "select * from orders where OnHold =1 order by order_id asc";
command.CommandText = strsql;
connection.Open(); Reader = command.ExecuteReader();
while (Reader.Read())
{
if (Reader["payment_date"].ToString() != "")
{
if (Reader["custom"].ToString().Contains("~*"))
{
CustomerName = Reader["custom"].ToString().Substring(0, Reader["custom"].ToString().IndexOf("~"));
}
else
{
CustomerName = Reader["shipping_address_name"].ToString();
}
grdHoldOrders.Invoke(new MethodInvoker(() => grdHoldOrders.Rows.Add(Convert.ToDateTime(Reader["payment_date"].ToString().Substring(0, 21)).ToString(), Reader["txn_id"].ToString(), CustomerName, Reader["mc_gross"].ToString(), Reader["memo"].ToString(), Reader["order_id"].ToString(),Reader["Hold_Review_Date"].ToString().Substring(0,Reader["Hold_Review_Date"].ToString().IndexOf(" ")),Reader["payer_email"].ToString())));
}
}
connection.Close();
}
}
Also, in case it's relevant, here is some code I use to color code the lines of the DataGridView object so we can easily tell which order is older:
private void grdOrderItems_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
grdOrderItems.RowPrePaint += new System.Windows.Forms.DataGridViewRowPrePaintEventHandler(this.grdOrderItems1_RowPrePaint);
}
private void grdOrderItems1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
if (e.RowIndex <= grdOrderItems.Rows.Count - 1)
{
string StringNow = DateTime.Now.ToString();
string NowTime = StringNow.Substring(StringNow.IndexOf(" ")+1, StringNow.Length- StringNow.IndexOf(" ")-1);
string OrderDateTime = grdOrderItems.Rows[e.RowIndex].Cells[0].Value.ToString().Substring(0, grdOrderItems.Rows[e.RowIndex].Cells[0].Value.ToString().IndexOf(" ")+1) + NowTime;
if ((Convert.ToDateTime(StringNow) - Convert.ToDateTime(OrderDateTime)).Days > 2)
{
grdOrderItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Tomato;
}
else
{
if ((Convert.ToDateTime(StringNow) - Convert.ToDateTime(OrderDateTime)).Days > 1)
{
grdOrderItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Yellow;
}
else
{
if ((Convert.ToDateTime(StringNow) - Convert.ToDateTime(OrderDateTime)).Days > 0)
{
grdOrderItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Tan;
}
}
}
}
}
c# mysql winforms datagridview timer
I have a program that updates two DataGridView objects every minute with a MySQL query from our internal orders database so we can keep track of older orders and make sure they're being handled.
It works well, but if you leave the program on it starts to get slower and slower and you really notice it after a day or so. Restarting the program fixes this, but I'd really like to know why I can't just leave this running.
Here is the timer code and the function it calls:
private void timer1_Tick(object sender, EventArgs e)
{
UpdateOrderDisplay();
}
private void UpdateOrderDisplay()
{
grdOrderItems.Rows.Clear();
grdHoldOrders.Rows.Clear();
string strsql;
string CustomerName;
string MyConString = "SERVER=**********;" + "DATABASE=***********;" + "UID=************;" + "PASSWORD=***********;";
using (MySqlConnection connection = new MySqlConnection(MyConString))
{
MySqlCommand command = connection.CreateCommand();
MySqlDataReader Reader;
strsql = "select * from orders where ship_reference=0 and OnHold =0 order by order_id asc";
command.CommandText = strsql;
connection.Open(); Reader = command.ExecuteReader();
while (Reader.Read())
{
if (Reader["payment_date"].ToString() != "")
{
if (Reader["custom"].ToString().Contains("~*"))
{
CustomerName = Reader["custom"].ToString().Substring(0, Reader["custom"].ToString().IndexOf("~"));
}
else
{
CustomerName = Reader["shipping_address_name"].ToString();
}
grdOrderItems.Invoke(new MethodInvoker(() => grdOrderItems.Rows.Add(Convert.ToDateTime(Reader["payment_date"].ToString().Substring(0, 21)).ToString(), Reader["txn_id"].ToString(), CustomerName, Reader["mc_gross"].ToString(), Reader["memo"].ToString(), Reader["order_id"].ToString())));
}
}
connection.Close();
strsql = "select * from orders where OnHold =1 order by order_id asc";
command.CommandText = strsql;
connection.Open(); Reader = command.ExecuteReader();
while (Reader.Read())
{
if (Reader["payment_date"].ToString() != "")
{
if (Reader["custom"].ToString().Contains("~*"))
{
CustomerName = Reader["custom"].ToString().Substring(0, Reader["custom"].ToString().IndexOf("~"));
}
else
{
CustomerName = Reader["shipping_address_name"].ToString();
}
grdHoldOrders.Invoke(new MethodInvoker(() => grdHoldOrders.Rows.Add(Convert.ToDateTime(Reader["payment_date"].ToString().Substring(0, 21)).ToString(), Reader["txn_id"].ToString(), CustomerName, Reader["mc_gross"].ToString(), Reader["memo"].ToString(), Reader["order_id"].ToString(),Reader["Hold_Review_Date"].ToString().Substring(0,Reader["Hold_Review_Date"].ToString().IndexOf(" ")),Reader["payer_email"].ToString())));
}
}
connection.Close();
}
}
Also, in case it's relevant, here is some code I use to color code the lines of the DataGridView object so we can easily tell which order is older:
private void grdOrderItems_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
grdOrderItems.RowPrePaint += new System.Windows.Forms.DataGridViewRowPrePaintEventHandler(this.grdOrderItems1_RowPrePaint);
}
private void grdOrderItems1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
if (e.RowIndex <= grdOrderItems.Rows.Count - 1)
{
string StringNow = DateTime.Now.ToString();
string NowTime = StringNow.Substring(StringNow.IndexOf(" ")+1, StringNow.Length- StringNow.IndexOf(" ")-1);
string OrderDateTime = grdOrderItems.Rows[e.RowIndex].Cells[0].Value.ToString().Substring(0, grdOrderItems.Rows[e.RowIndex].Cells[0].Value.ToString().IndexOf(" ")+1) + NowTime;
if ((Convert.ToDateTime(StringNow) - Convert.ToDateTime(OrderDateTime)).Days > 2)
{
grdOrderItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Tomato;
}
else
{
if ((Convert.ToDateTime(StringNow) - Convert.ToDateTime(OrderDateTime)).Days > 1)
{
grdOrderItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Yellow;
}
else
{
if ((Convert.ToDateTime(StringNow) - Convert.ToDateTime(OrderDateTime)).Days > 0)
{
grdOrderItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Tan;
}
}
}
}
}
c# mysql winforms datagridview timer
c# mysql winforms datagridview timer
asked Nov 13 '18 at 18:08
Alan DenkeAlan Denke
325
325
Dispose thoseCommand
s.
– GSerg
Nov 13 '18 at 18:13
1
Yeah, close the reader explicitly after use (or put it in ausing
), would be my first thing to change.
– 500 - Internal Server Error
Nov 13 '18 at 18:15
3
Is thegrdOrderItems_RowPrePaint
an actual live handler that also fires? If so, you are adding another subscriber togrdOrderItems.RowPrePaint
each time it fires, and after a while eachgrdOrderItems.RowPrePaint
will cause thousands of executions ofthis.grdOrderItems1_RowPrePaint
.
– GSerg
Nov 13 '18 at 18:15
1
@MatheusRocha No it won't. It will continue to exist until collected by the garbage collector, which may not happen in a while. That is precisely the reason whyIDisposable
exists.
– GSerg
Nov 13 '18 at 18:23
1
You don't need to close and re-open the connection in between the two commands.
– Joel Coehoorn
Nov 13 '18 at 18:31
|
show 10 more comments
Dispose thoseCommand
s.
– GSerg
Nov 13 '18 at 18:13
1
Yeah, close the reader explicitly after use (or put it in ausing
), would be my first thing to change.
– 500 - Internal Server Error
Nov 13 '18 at 18:15
3
Is thegrdOrderItems_RowPrePaint
an actual live handler that also fires? If so, you are adding another subscriber togrdOrderItems.RowPrePaint
each time it fires, and after a while eachgrdOrderItems.RowPrePaint
will cause thousands of executions ofthis.grdOrderItems1_RowPrePaint
.
– GSerg
Nov 13 '18 at 18:15
1
@MatheusRocha No it won't. It will continue to exist until collected by the garbage collector, which may not happen in a while. That is precisely the reason whyIDisposable
exists.
– GSerg
Nov 13 '18 at 18:23
1
You don't need to close and re-open the connection in between the two commands.
– Joel Coehoorn
Nov 13 '18 at 18:31
Dispose those
Command
s.– GSerg
Nov 13 '18 at 18:13
Dispose those
Command
s.– GSerg
Nov 13 '18 at 18:13
1
1
Yeah, close the reader explicitly after use (or put it in a
using
), would be my first thing to change.– 500 - Internal Server Error
Nov 13 '18 at 18:15
Yeah, close the reader explicitly after use (or put it in a
using
), would be my first thing to change.– 500 - Internal Server Error
Nov 13 '18 at 18:15
3
3
Is the
grdOrderItems_RowPrePaint
an actual live handler that also fires? If so, you are adding another subscriber to grdOrderItems.RowPrePaint
each time it fires, and after a while each grdOrderItems.RowPrePaint
will cause thousands of executions of this.grdOrderItems1_RowPrePaint
.– GSerg
Nov 13 '18 at 18:15
Is the
grdOrderItems_RowPrePaint
an actual live handler that also fires? If so, you are adding another subscriber to grdOrderItems.RowPrePaint
each time it fires, and after a while each grdOrderItems.RowPrePaint
will cause thousands of executions of this.grdOrderItems1_RowPrePaint
.– GSerg
Nov 13 '18 at 18:15
1
1
@MatheusRocha No it won't. It will continue to exist until collected by the garbage collector, which may not happen in a while. That is precisely the reason why
IDisposable
exists.– GSerg
Nov 13 '18 at 18:23
@MatheusRocha No it won't. It will continue to exist until collected by the garbage collector, which may not happen in a while. That is precisely the reason why
IDisposable
exists.– GSerg
Nov 13 '18 at 18:23
1
1
You don't need to close and re-open the connection in between the two commands.
– Joel Coehoorn
Nov 13 '18 at 18:31
You don't need to close and re-open the connection in between the two commands.
– Joel Coehoorn
Nov 13 '18 at 18:31
|
show 10 more comments
2 Answers
2
active
oldest
votes
You probably have a MySqlDataReader leak. When you're done with each Reader
object, dispose of it. This is a good way to make that happen; it's resilient against exceptions, and the reader
variable goes out of scope at the end of the using.
using (var reader = command.ExecuteReader()
{
while (reader.Read())
{
/* your per-row logic here */
}
}
You can also do this with Close():
MySqlDataReader Reader;
...
Reader = command.ExecuteReader()
while (Reader.Read())
{
/* your per-row logic here */
}
Reader.Close();
You are closing and reopening the Connection for each query. That's not necessary. Open it once, use it for both queries, and then close it.
You might consider keeping your Connection open for longer than a single timer tick. (If you have a connection pool, ignore this advice.)
Use the Task Manager to see how much cpu% and memory your program takes up (in the morning and again in the evening). If the memory is growing, you have some kind of leak. If the cpu% is growing, you have some sort of list processing going on where the list gets longer with every tick.
For the last, it depends on the tick length, and a full minute connection pooling means he probably really does want a new connection object with each tick. In my experience, anything more than a few seconds idle and it's best to let the prior connection object dispose. But you're right that he should be able to re-use a single connection for the whole tick even handler method.
– Joel Coehoorn
Nov 13 '18 at 18:33
1
A red note on theRowPrePaint
event that adds a new handler (grdOrderItems.RowPrePaint += new (...)
) each time is raised. That's going to kill everything. I'm surprised it goes on a whole day.
– Jimi
Nov 13 '18 at 18:48
@Jimi You should add that as an answer. I missed that first time through, but it's definitely a factor, if not the entire problem.
– Joel Coehoorn
Nov 13 '18 at 19:02
@Joel Coehoorn It would be interesting to ask the OP whether this is some kind of style and it's, possibly, applied elsewhere. Anyway, there are already two answers to this question and, now I see, a bunch of comments that point this out. You two should pick it up, I think.
– Jimi
Nov 13 '18 at 19:15
add a comment |
I don't have enough info for a complete answer, except to say this smells like a large object heap issue, where one of the fields you retrieve from the database will occasionally be longer than the 85K LOH threshold.
What I can do, and it might even help, is show you how to greatly simplify and improve the prepaint method like this:
private void grdOrderItems1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
if (e.RowIndex > grdOrderItems.Rows.Count - 1) return;
var row = grdOrderItems.Rows[e.RowIndex];
TimeSpan timeOfDay = DateTime.Now - DateTime.Today;
//It *looks like* Cells[0].Value is already a DateTime, but I'm not 100% on this
// If I'm wrong and it's a string, its worth it to parse just that one value to a DateTime here, and still plug that DateTime value into this code instead of the Cell.
DateTime OrderDateTime = ((DateTime)row.Cells[0].Value).Date + timeOfDay;
var days = (DateTime.Now - OrderDateTime).TotalDays;
if (days > 2)
{
row.DefaultCellStyle.BackColor = Color.Tomato;
}
else if (days > 1)
{
row.DefaultCellStyle.BackColor = Color.Yellow;
}
else if (days > 0)
{
row.DefaultCellStyle.BackColor = Color.Tan;
}
}
This reduces nesting and extra blocks, making it a lot easier to understand, and it probably runs in less than half the time because of the reduced back-and-forth between string and DateTime values. The cultural/internationalization issues make converting between DateTime and string in either direction an inherently slow and error-prone operation.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53287079%2fwhy-does-my-c-sharp-winforms-program-with-a-timer-get-bogged-down-after-a-day%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
You probably have a MySqlDataReader leak. When you're done with each Reader
object, dispose of it. This is a good way to make that happen; it's resilient against exceptions, and the reader
variable goes out of scope at the end of the using.
using (var reader = command.ExecuteReader()
{
while (reader.Read())
{
/* your per-row logic here */
}
}
You can also do this with Close():
MySqlDataReader Reader;
...
Reader = command.ExecuteReader()
while (Reader.Read())
{
/* your per-row logic here */
}
Reader.Close();
You are closing and reopening the Connection for each query. That's not necessary. Open it once, use it for both queries, and then close it.
You might consider keeping your Connection open for longer than a single timer tick. (If you have a connection pool, ignore this advice.)
Use the Task Manager to see how much cpu% and memory your program takes up (in the morning and again in the evening). If the memory is growing, you have some kind of leak. If the cpu% is growing, you have some sort of list processing going on where the list gets longer with every tick.
For the last, it depends on the tick length, and a full minute connection pooling means he probably really does want a new connection object with each tick. In my experience, anything more than a few seconds idle and it's best to let the prior connection object dispose. But you're right that he should be able to re-use a single connection for the whole tick even handler method.
– Joel Coehoorn
Nov 13 '18 at 18:33
1
A red note on theRowPrePaint
event that adds a new handler (grdOrderItems.RowPrePaint += new (...)
) each time is raised. That's going to kill everything. I'm surprised it goes on a whole day.
– Jimi
Nov 13 '18 at 18:48
@Jimi You should add that as an answer. I missed that first time through, but it's definitely a factor, if not the entire problem.
– Joel Coehoorn
Nov 13 '18 at 19:02
@Joel Coehoorn It would be interesting to ask the OP whether this is some kind of style and it's, possibly, applied elsewhere. Anyway, there are already two answers to this question and, now I see, a bunch of comments that point this out. You two should pick it up, I think.
– Jimi
Nov 13 '18 at 19:15
add a comment |
You probably have a MySqlDataReader leak. When you're done with each Reader
object, dispose of it. This is a good way to make that happen; it's resilient against exceptions, and the reader
variable goes out of scope at the end of the using.
using (var reader = command.ExecuteReader()
{
while (reader.Read())
{
/* your per-row logic here */
}
}
You can also do this with Close():
MySqlDataReader Reader;
...
Reader = command.ExecuteReader()
while (Reader.Read())
{
/* your per-row logic here */
}
Reader.Close();
You are closing and reopening the Connection for each query. That's not necessary. Open it once, use it for both queries, and then close it.
You might consider keeping your Connection open for longer than a single timer tick. (If you have a connection pool, ignore this advice.)
Use the Task Manager to see how much cpu% and memory your program takes up (in the morning and again in the evening). If the memory is growing, you have some kind of leak. If the cpu% is growing, you have some sort of list processing going on where the list gets longer with every tick.
For the last, it depends on the tick length, and a full minute connection pooling means he probably really does want a new connection object with each tick. In my experience, anything more than a few seconds idle and it's best to let the prior connection object dispose. But you're right that he should be able to re-use a single connection for the whole tick even handler method.
– Joel Coehoorn
Nov 13 '18 at 18:33
1
A red note on theRowPrePaint
event that adds a new handler (grdOrderItems.RowPrePaint += new (...)
) each time is raised. That's going to kill everything. I'm surprised it goes on a whole day.
– Jimi
Nov 13 '18 at 18:48
@Jimi You should add that as an answer. I missed that first time through, but it's definitely a factor, if not the entire problem.
– Joel Coehoorn
Nov 13 '18 at 19:02
@Joel Coehoorn It would be interesting to ask the OP whether this is some kind of style and it's, possibly, applied elsewhere. Anyway, there are already two answers to this question and, now I see, a bunch of comments that point this out. You two should pick it up, I think.
– Jimi
Nov 13 '18 at 19:15
add a comment |
You probably have a MySqlDataReader leak. When you're done with each Reader
object, dispose of it. This is a good way to make that happen; it's resilient against exceptions, and the reader
variable goes out of scope at the end of the using.
using (var reader = command.ExecuteReader()
{
while (reader.Read())
{
/* your per-row logic here */
}
}
You can also do this with Close():
MySqlDataReader Reader;
...
Reader = command.ExecuteReader()
while (Reader.Read())
{
/* your per-row logic here */
}
Reader.Close();
You are closing and reopening the Connection for each query. That's not necessary. Open it once, use it for both queries, and then close it.
You might consider keeping your Connection open for longer than a single timer tick. (If you have a connection pool, ignore this advice.)
Use the Task Manager to see how much cpu% and memory your program takes up (in the morning and again in the evening). If the memory is growing, you have some kind of leak. If the cpu% is growing, you have some sort of list processing going on where the list gets longer with every tick.
You probably have a MySqlDataReader leak. When you're done with each Reader
object, dispose of it. This is a good way to make that happen; it's resilient against exceptions, and the reader
variable goes out of scope at the end of the using.
using (var reader = command.ExecuteReader()
{
while (reader.Read())
{
/* your per-row logic here */
}
}
You can also do this with Close():
MySqlDataReader Reader;
...
Reader = command.ExecuteReader()
while (Reader.Read())
{
/* your per-row logic here */
}
Reader.Close();
You are closing and reopening the Connection for each query. That's not necessary. Open it once, use it for both queries, and then close it.
You might consider keeping your Connection open for longer than a single timer tick. (If you have a connection pool, ignore this advice.)
Use the Task Manager to see how much cpu% and memory your program takes up (in the morning and again in the evening). If the memory is growing, you have some kind of leak. If the cpu% is growing, you have some sort of list processing going on where the list gets longer with every tick.
edited Nov 13 '18 at 18:42
answered Nov 13 '18 at 18:25
O. JonesO. Jones
59.8k972106
59.8k972106
For the last, it depends on the tick length, and a full minute connection pooling means he probably really does want a new connection object with each tick. In my experience, anything more than a few seconds idle and it's best to let the prior connection object dispose. But you're right that he should be able to re-use a single connection for the whole tick even handler method.
– Joel Coehoorn
Nov 13 '18 at 18:33
1
A red note on theRowPrePaint
event that adds a new handler (grdOrderItems.RowPrePaint += new (...)
) each time is raised. That's going to kill everything. I'm surprised it goes on a whole day.
– Jimi
Nov 13 '18 at 18:48
@Jimi You should add that as an answer. I missed that first time through, but it's definitely a factor, if not the entire problem.
– Joel Coehoorn
Nov 13 '18 at 19:02
@Joel Coehoorn It would be interesting to ask the OP whether this is some kind of style and it's, possibly, applied elsewhere. Anyway, there are already two answers to this question and, now I see, a bunch of comments that point this out. You two should pick it up, I think.
– Jimi
Nov 13 '18 at 19:15
add a comment |
For the last, it depends on the tick length, and a full minute connection pooling means he probably really does want a new connection object with each tick. In my experience, anything more than a few seconds idle and it's best to let the prior connection object dispose. But you're right that he should be able to re-use a single connection for the whole tick even handler method.
– Joel Coehoorn
Nov 13 '18 at 18:33
1
A red note on theRowPrePaint
event that adds a new handler (grdOrderItems.RowPrePaint += new (...)
) each time is raised. That's going to kill everything. I'm surprised it goes on a whole day.
– Jimi
Nov 13 '18 at 18:48
@Jimi You should add that as an answer. I missed that first time through, but it's definitely a factor, if not the entire problem.
– Joel Coehoorn
Nov 13 '18 at 19:02
@Joel Coehoorn It would be interesting to ask the OP whether this is some kind of style and it's, possibly, applied elsewhere. Anyway, there are already two answers to this question and, now I see, a bunch of comments that point this out. You two should pick it up, I think.
– Jimi
Nov 13 '18 at 19:15
For the last, it depends on the tick length, and a full minute connection pooling means he probably really does want a new connection object with each tick. In my experience, anything more than a few seconds idle and it's best to let the prior connection object dispose. But you're right that he should be able to re-use a single connection for the whole tick even handler method.
– Joel Coehoorn
Nov 13 '18 at 18:33
For the last, it depends on the tick length, and a full minute connection pooling means he probably really does want a new connection object with each tick. In my experience, anything more than a few seconds idle and it's best to let the prior connection object dispose. But you're right that he should be able to re-use a single connection for the whole tick even handler method.
– Joel Coehoorn
Nov 13 '18 at 18:33
1
1
A red note on the
RowPrePaint
event that adds a new handler (grdOrderItems.RowPrePaint += new (...)
) each time is raised. That's going to kill everything. I'm surprised it goes on a whole day.– Jimi
Nov 13 '18 at 18:48
A red note on the
RowPrePaint
event that adds a new handler (grdOrderItems.RowPrePaint += new (...)
) each time is raised. That's going to kill everything. I'm surprised it goes on a whole day.– Jimi
Nov 13 '18 at 18:48
@Jimi You should add that as an answer. I missed that first time through, but it's definitely a factor, if not the entire problem.
– Joel Coehoorn
Nov 13 '18 at 19:02
@Jimi You should add that as an answer. I missed that first time through, but it's definitely a factor, if not the entire problem.
– Joel Coehoorn
Nov 13 '18 at 19:02
@Joel Coehoorn It would be interesting to ask the OP whether this is some kind of style and it's, possibly, applied elsewhere. Anyway, there are already two answers to this question and, now I see, a bunch of comments that point this out. You two should pick it up, I think.
– Jimi
Nov 13 '18 at 19:15
@Joel Coehoorn It would be interesting to ask the OP whether this is some kind of style and it's, possibly, applied elsewhere. Anyway, there are already two answers to this question and, now I see, a bunch of comments that point this out. You two should pick it up, I think.
– Jimi
Nov 13 '18 at 19:15
add a comment |
I don't have enough info for a complete answer, except to say this smells like a large object heap issue, where one of the fields you retrieve from the database will occasionally be longer than the 85K LOH threshold.
What I can do, and it might even help, is show you how to greatly simplify and improve the prepaint method like this:
private void grdOrderItems1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
if (e.RowIndex > grdOrderItems.Rows.Count - 1) return;
var row = grdOrderItems.Rows[e.RowIndex];
TimeSpan timeOfDay = DateTime.Now - DateTime.Today;
//It *looks like* Cells[0].Value is already a DateTime, but I'm not 100% on this
// If I'm wrong and it's a string, its worth it to parse just that one value to a DateTime here, and still plug that DateTime value into this code instead of the Cell.
DateTime OrderDateTime = ((DateTime)row.Cells[0].Value).Date + timeOfDay;
var days = (DateTime.Now - OrderDateTime).TotalDays;
if (days > 2)
{
row.DefaultCellStyle.BackColor = Color.Tomato;
}
else if (days > 1)
{
row.DefaultCellStyle.BackColor = Color.Yellow;
}
else if (days > 0)
{
row.DefaultCellStyle.BackColor = Color.Tan;
}
}
This reduces nesting and extra blocks, making it a lot easier to understand, and it probably runs in less than half the time because of the reduced back-and-forth between string and DateTime values. The cultural/internationalization issues make converting between DateTime and string in either direction an inherently slow and error-prone operation.
add a comment |
I don't have enough info for a complete answer, except to say this smells like a large object heap issue, where one of the fields you retrieve from the database will occasionally be longer than the 85K LOH threshold.
What I can do, and it might even help, is show you how to greatly simplify and improve the prepaint method like this:
private void grdOrderItems1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
if (e.RowIndex > grdOrderItems.Rows.Count - 1) return;
var row = grdOrderItems.Rows[e.RowIndex];
TimeSpan timeOfDay = DateTime.Now - DateTime.Today;
//It *looks like* Cells[0].Value is already a DateTime, but I'm not 100% on this
// If I'm wrong and it's a string, its worth it to parse just that one value to a DateTime here, and still plug that DateTime value into this code instead of the Cell.
DateTime OrderDateTime = ((DateTime)row.Cells[0].Value).Date + timeOfDay;
var days = (DateTime.Now - OrderDateTime).TotalDays;
if (days > 2)
{
row.DefaultCellStyle.BackColor = Color.Tomato;
}
else if (days > 1)
{
row.DefaultCellStyle.BackColor = Color.Yellow;
}
else if (days > 0)
{
row.DefaultCellStyle.BackColor = Color.Tan;
}
}
This reduces nesting and extra blocks, making it a lot easier to understand, and it probably runs in less than half the time because of the reduced back-and-forth between string and DateTime values. The cultural/internationalization issues make converting between DateTime and string in either direction an inherently slow and error-prone operation.
add a comment |
I don't have enough info for a complete answer, except to say this smells like a large object heap issue, where one of the fields you retrieve from the database will occasionally be longer than the 85K LOH threshold.
What I can do, and it might even help, is show you how to greatly simplify and improve the prepaint method like this:
private void grdOrderItems1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
if (e.RowIndex > grdOrderItems.Rows.Count - 1) return;
var row = grdOrderItems.Rows[e.RowIndex];
TimeSpan timeOfDay = DateTime.Now - DateTime.Today;
//It *looks like* Cells[0].Value is already a DateTime, but I'm not 100% on this
// If I'm wrong and it's a string, its worth it to parse just that one value to a DateTime here, and still plug that DateTime value into this code instead of the Cell.
DateTime OrderDateTime = ((DateTime)row.Cells[0].Value).Date + timeOfDay;
var days = (DateTime.Now - OrderDateTime).TotalDays;
if (days > 2)
{
row.DefaultCellStyle.BackColor = Color.Tomato;
}
else if (days > 1)
{
row.DefaultCellStyle.BackColor = Color.Yellow;
}
else if (days > 0)
{
row.DefaultCellStyle.BackColor = Color.Tan;
}
}
This reduces nesting and extra blocks, making it a lot easier to understand, and it probably runs in less than half the time because of the reduced back-and-forth between string and DateTime values. The cultural/internationalization issues make converting between DateTime and string in either direction an inherently slow and error-prone operation.
I don't have enough info for a complete answer, except to say this smells like a large object heap issue, where one of the fields you retrieve from the database will occasionally be longer than the 85K LOH threshold.
What I can do, and it might even help, is show you how to greatly simplify and improve the prepaint method like this:
private void grdOrderItems1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
if (e.RowIndex > grdOrderItems.Rows.Count - 1) return;
var row = grdOrderItems.Rows[e.RowIndex];
TimeSpan timeOfDay = DateTime.Now - DateTime.Today;
//It *looks like* Cells[0].Value is already a DateTime, but I'm not 100% on this
// If I'm wrong and it's a string, its worth it to parse just that one value to a DateTime here, and still plug that DateTime value into this code instead of the Cell.
DateTime OrderDateTime = ((DateTime)row.Cells[0].Value).Date + timeOfDay;
var days = (DateTime.Now - OrderDateTime).TotalDays;
if (days > 2)
{
row.DefaultCellStyle.BackColor = Color.Tomato;
}
else if (days > 1)
{
row.DefaultCellStyle.BackColor = Color.Yellow;
}
else if (days > 0)
{
row.DefaultCellStyle.BackColor = Color.Tan;
}
}
This reduces nesting and extra blocks, making it a lot easier to understand, and it probably runs in less than half the time because of the reduced back-and-forth between string and DateTime values. The cultural/internationalization issues make converting between DateTime and string in either direction an inherently slow and error-prone operation.
edited Nov 13 '18 at 18:54
answered Nov 13 '18 at 18:48
Joel CoehoornJoel Coehoorn
307k95490721
307k95490721
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53287079%2fwhy-does-my-c-sharp-winforms-program-with-a-timer-get-bogged-down-after-a-day%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Dispose those
Command
s.– GSerg
Nov 13 '18 at 18:13
1
Yeah, close the reader explicitly after use (or put it in a
using
), would be my first thing to change.– 500 - Internal Server Error
Nov 13 '18 at 18:15
3
Is the
grdOrderItems_RowPrePaint
an actual live handler that also fires? If so, you are adding another subscriber togrdOrderItems.RowPrePaint
each time it fires, and after a while eachgrdOrderItems.RowPrePaint
will cause thousands of executions ofthis.grdOrderItems1_RowPrePaint
.– GSerg
Nov 13 '18 at 18:15
1
@MatheusRocha No it won't. It will continue to exist until collected by the garbage collector, which may not happen in a while. That is precisely the reason why
IDisposable
exists.– GSerg
Nov 13 '18 at 18:23
1
You don't need to close and re-open the connection in between the two commands.
– Joel Coehoorn
Nov 13 '18 at 18:31