October 2008 - Posts
Since today is Halloween I thought I’d share a scary ghost bug story that I ran into this week. What better way to get everyone into the Halloween spirit?
Background:
- Running a load test, using one webtest, to gauge how the application handles heavy loads for a very specific scenario
Problem:
- Application is throwing an average of 4 exceptions/sec per user spiking CPU usage
Story:
- Ran load test and confirmed baseline of ~ 4 exceptions/sec per user on average
- Reviewed code and saw that Response.Redirect(url) was being used
- Replaced Response.Redirect(url) with Response.Redirect(url, false) and ran load test
- Saw improvement to ~3 exceptions/sec per user
- Added break point in custom error handler, to see the exceptions being caught, and ran load test
- Caught a FileNotFound exception with no valuable information
- Checked the stack trace and saw “System.Web.StaticFileHandler”
- Guessed that there must have been a problem loading a .css or image file which are static
- Used ieHttpHeaders and saw a funky GET: GET /~/foo/bar.css
- Fixed the style sheet reference and ran load test
- Saw improvement to ~2 exceptions/sec per user
- Ran load test again
- Caught an EventValidation exception
- Observed custom error handler throwing a NullReferenceException because Server.GetLastError() was null but there was no check before calling Server.GetLastError.GetBaseException()
- Added null check
- Ran load test and saw improvement to ~1 exception/sec per user
- Disabled EventValidation on pages and ran load test and saw improvement to ~0 exceptions/sec per users
- Enabled EventValidation on pages because I didn’t like the solution and began investigating
- 30mins later –I was baffled
- Manually ran the webtest and did not get any EventValidation exceptions
- Eureka! moment – checked webtest and saw that the ThinkTime between requests was set to 0 causing the next page request to be called before the first page request had finished loading
- Changed ThinkTime between request to 5sec
- App now had ~0 exceptions/sec per user
Ok, so it wasn’t that scary but it was painful! Happy Halloween!
OK, I made the title a little misleading to grab your attention - I'm sneaky like that. Seriously though, there are a few things about Response.Redirect(url) that every ASP .NET developer needs to know.
Using Response.Redirect() to help with site navigation is standard and all ASP .NET developers use it but, most don’t have time to stop and think about what happens under the hood. Put simply, every call to Response.Redirect(url) will throw an Exception and that’s bad…or is it?
Here is a snippet from a w3wp.exe process dump:
Exception type: System.Threading.ThreadAbortException
Message: Thread was being aborted.
InnerException: <none>
StackTrace (generated):
SP IP Function
000000000643E2F0 0000000000000001 System.Threading.Thread.AbortInternal()
000000000643E2F0 0000098278342F1A System.Threading.Thread.Abort(System.Object)
000000000643E340 00000982BC912E96 System.Web.HttpResponse.End()
This snippet shows that a call to Response.Redirect(url) asks .NET to stop processing the current web page thread and to transfer execution to another thread. Internally a call is made to Response.End() which calls Thread.Abort() which throws an exception.
Is this good, or bad, or ugly? It’s debatable so I won’t go there. All I'll say is that It would have been nice if there was some other way to notify .NET that a thread needed to be stopped besides throwing an exception but, now that the problem is understood let’s work with it and not against it.
Work with it by using Response.Redirect()’s overload that accepts a bool to determine if the request should be ended. Calling Response.Redirect(url, false) asks .NET not to abort the thread and as a result the call to Response.End() is skipped and the ThreadAbortException is avoided.
Is it that simple? Nope. There is a price to pay for not aborting the thread. All the page events and postback events will be processed, and the page will still send its HTML to the browser. Let me say that one more time, all the page events and postback events will be processed. This means any DB calls or complex calculations will still be performed.
So, what are your choices? Either program defensively by setting Boolean flags to ensure that events are not called by accident or, compromise by limiting the use of Response.Redirect, by ensuring that any call to Response.Redirect(url, false) is wrapped in an if-else block or returns immediately after the call and, most importantly, by analyzing the code to see what impact the call will have.
Using the built-in ASP .NET validation controls makes validation a breeze. The controls are an easy and powerful way to validate form data client-side and server-side. If you need to display a summarized list of all the validation errors on a page you can use the ValidationSummary control. Unfortunately, as with most out of the box controls, you'll have to sacrifice the ability to style it exactly how you want.

Figure 1 - Boring ol' ValidationSummary
Figure 1 shows the ValidationSummary consists of two parts: the header text, and the error messages. The control does not allow you to specify a custom css class for either of these text elements, nor does it allow you to specify an image. So, how do you create a ValidationSummary with some style?

Figure 2 - Stylin' and profilin'
A few weeks ago I was asked to create something similar to figure 2. I wanted to avoid creating a custom control because I wasn't adding any functionality. Moreover, creating a custom control from scratch or extending the existing ValidationSummary meant I'd have to code both the client-side and server-side functionality -- I really didn't want to touch WebUIValidation.js.
To start, I played with the header text to see if I could inject some HTML, and I could.

Figure 3 - Injecting HTML into the HeaderText
Injecting HTML allowed me to wrap the header text and error messages in span tags and assign a css class as shown above. Next, I needed to add an image to the ValidationSummary.
My first attempt at adding an image to the ValidationSummary was to set the CssClass for the control and use the image as the background-image of the div. I used the following css:

Figure 4 - Setting the image as the background
It worked! But, when I tested different display modes and edge cases I found, in certain situations, the error messages would not align correctly. I couldn't fix this because I had no way of wrapping all the error messages in a div. I also tried adding the image using HTML in the header text but ran into a similar problem.

Figure 5 - Alignment problem
To fix the alignment problem I created a two column layout. I placed the image in the left column and the ValidationSummary in the right column. Next I whipped up some Javascript to tie the image and ValidationSummary together so they would show/hide in unison.

Figure 6 - Two column layout

Figure 7 - Javascript to show/hide image
I used the client-side method Page_ClientValidate which let me test if my controls met the validation criteria. In this case I also passed a validation group so it didn't interfere with any other validation on the page. I hooked up my button's OnClientClick to call the Javascript and there it was...a ValidationSummary with some style.
One word of caution, since the image is only tied to the ValidationSummary using Javascript you'll need to write some server-side code to show/hide the image in case the user has disabled Javascript.
A few weeks ago I needed to create a step-by-step wizard that required more functionality than the built-in Wizard control provided out of the box. I created my custom wizard by taking advantage of the MultiView control which is essentially what the Wizard control is based on. After creating my custom wizard I tackled a requirement that specified all validation had to occur on the final step of the wizard when the user clicked "Finish". I designed the wizard so each view would have validators with a ValidationGroup unique to that view. My original plan was to call the following code:

Unfortunately, this code will not return the correct result. Why? Well after some investigating it turns out validation controls in a hidden View will not be validated and checking IsValid on those controls will always return true. It would be interesting to find out why this design decision was made.
The following code will get around this:

One caveat to this method is that the MultiView's ActiveViewChanged event will fire so you'll need to take that into account if you are using it.
Today I came across a situation where iterating through an Enum would save me about 10 lines of code and some time. The quickest way is as follows:
foreach
(MyEnum value in Enum.GetValues(typeof(MyEnum)))
{
//...
}
More Posts