We have a fairly complex form in our ASP.Net 2.0 app. It has a few buttons on it that load various modal selection windows for different data items.
Bug raised: Fill in all the data, including using these three popup windows, hit save, and the Drop Downs on the screen clear out.
Debugging:
We worked out pretty early that those dropdowns were losing their ViewState.
Clicking any 3 combinations of the buttons is enough to repro the bug (ie 1 button 3 times), any 2 buttons is fine, as is one button twice.
Closing the selection windows rather than selecting something worked fine, so we started suspecting some sort of dynamic data updating issue, a la EventValidation.
Because we use the SessionStatePersister, some deep gut instinct made me disable this back to the default HiddenFieldPageStatePersister, at which point everything started working correctly.... hmmm....
So we know it's related to ViewState, but why this magical threshold of 3 Clicks??
Brilliant co-worker #1 wasn't happy with my (totally pragmatic) workaround of just setting that one page to use the HiddenFieldPersister, so he dug deeper.
Using Reflector, a little bit of code caught his eye:
if (
(sessionPageState != null && count > sessionPageState.HistorySize)
||
(sessionPageState == null && count > 9)
) {
string name = (string) queue.Dequeue();
session.Remove(name);
}
In other words, because the data stored by the SessionPersister is survives past the use of each page, it stores the data in a queue, and clears the old data as it fills up (as Rico said - "A cache with a bad clearance policy is another name for a memory leak").
As the code snippet above implies, the default queue length is 9.
So with our page:
1. Main Page Load
2. Popup #1 Load
3. Popup #1 Postback
4. Popup#1 Select
5. Popup #2 Load
6. Popup #2 Postback
7. Popup#2 Select
8. Popup #3 Load
9. Popup #3 Postback
10. Popup#3 Select
11. Main Page Postback
The observant will notice that 11 > 9, and so our main page's viewstate wasn't available in session when it needed it!!
Turns out it's easy to fix too, just add:
to the system.web section of your config file.
<sessionpagestate historysize="number">
It added a few grey hairs though!