Team LiB
Previous Section Next Section

Customizing Asserts with a TraceListener

The Debug and Trace classes can easily be customized to behave any way you want. Rather than actually doing the work of the assert themselves, these classes merely delegate to a helper class called System.Diagnostics.TraceListener. We'll discuss

TraceListeners further in Chapter 5, but for now, you just need to know that by writing your own TraceListener class, you can customize how your asserts behave. For instance, if you wanted your asserts to e-mail you rather than displaying an error message, you could easily do that.

Let's write a TraceListener class to customize our asserts for ASP.NET. (Don't worry if you don't yet fully understand how to use TraceListener—we'll come back to the topic in the next chapter.) Remember, our goal for ASP.NET asserts is to display them when the user is browsing the page from the web server machine, but not when the user is browsing from a different machine. To detect that, we just need to compare the IP address of the web server with the IP address of the web request.

Caution 

Technically, detecting the IP address can be slightly complicated—some advanced machines may have multiple NICs that create multiple IP addresses. But we'll ignore that here since asserts are only needed on development machines anyway. If your development machine has multiple NICs, then you can take that into account during your testing.

After checking the IP address of the web request, we need to display an assert dialog box in a way that the service-based nature of ASP.NET won't reject. It turns out this can be accomplished by using the MessageBoxOptions.ServiceNotification flag as demonstrated here:

using System;
using System.Web;
using System.Diagnostics;
using System.Windows.Forms;
public class AssertClass : DefaultTraceListener {
    public override void Fail(string message) {
        Fail(message, null);
    }
    public override void Fail(string message1, string message2) {
        HttpRequest req = HttpContext.Current.Request;
        //If this object is being used in a non-web based application,
        // then we can safely display an assert. Otherwise, only
        // show assert if user is logged in to local machine.
        if (req == null ||
            req.UserHostName == req.ServerVariables.Get("LOCAL_ADDR")) {
            //Display the stack trace of the failed assert
            StackTrace t = new StackTrace();
            string s = t.ToString();
            string displayMe = message1 + "\n" + message2 + "\n" +
                "Launch debugger?+\n\n" + t.ToString();
            DialogResult r =
                MessageBox.Show(displayMe, "Assert failed!",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Error,
                    MessageBoxDefaultButton.Button1,
                    MessageBoxOptions.DefaultDesktopOnly);
                    //If the user pushed yes, launch the debugger
                    if (r == DialogResult.Yes)
                            Debugger.Break();
                }
        }
}

Wasn't that easy? The Debug and Trace classes are designed to delegate calls of the Assert method to the TraceListener's Fail method. There aren't any tricks to the preceding code—we just check if the user is on the local machine, and if so, then we display a message box using the MessageBoxOptions.DefaultDesktopOnly flag. Finally, if the user asks, then we'll launch the debugger. A very few simple lines and now we can use asserts in our ASP.NET pages and Web Services. All that remains is to tell ASP.NET to use our new TraceListener class instead of the default. We can do this in our ASP.NET application's startup method:

using System.Diagnostics;
public class Global : System.Web.HttpApplication {
    ...
    protected void Application_Start(Object sender, EventArgs e) {
        Debug.Listeners.Clear();
        Debug.Listeners.Add(new AssertClass());
    }
}

When the Customized Assert Fires

Our customized assert looks very much like the standard assert (see Figure 4-3).

Click To expand
Figure 4-3: The customized assert

If you accept the option to launch the debugger from that assert, then Visual Studio.NET will ask you what type of debugger you wish to use. Accept all the defaults, and you'll be dropped into the VS.NET debugger. There's only one thing to be aware of. As shown in Figure 4-4, the debugger will be started at your assert code's call to Debug.Break (which you don't have debugging symbols for anyway), whereas you want to start debugging at the function that invoked the assert. That's easily solved. Just press the "Step out of" debugging button a couple times until the call stack jumps up to the place you want to be.

Click To expand
Figure 4-4: The call stack after invoking our customized assert

Using Customized Asserts in a Service

Modifying this assert to work with services is easier or harder, depending on how your service will be used. We were able to detect that the user and ASP.NET page were on the same machine by using the HttpRequest class. However, that trick won't work for service requests. If your service will only be accessed by users logged on to the local machine, then you can assume the assert should always be raised in Debug mode, and simply shorten the TraceListener code to always display the assert message box. But if users might be connecting to this service from other machines, then you need to be careful or else those users might get confused when an assert on the service machine causes their client process to hang, forever waiting on a response.

But even if you find using asserts difficult in a few situations, don't let that blind you to the thousands of other situations where asserts can be one of your most powerful weapons. Few other techniques can pinpoint bugs as effectively as debug asserts, and learning to use asserts correctly is a skill every programmer should develop.


Team LiB
Previous Section Next Section