| Asserts - key to
the Debugging Palace
Debug.Assert() is not well known in the .NET programming space.
However, it is easy to understand and can be one of the most useful tools
in your arsenal when writing and debugging .NET code.
To simplify, the way Assert works is that when your code runs, you expect
some variable or expression at a particular point in your code to always
be true. If the condition is ever not true, you want to know about it
right away because you may have a bug. Assert statements give you a productive
way to zero in on the exact point of failure, and ONLY at the exact
point in time that the failure has actually occurred, such as
the sixteenth time through a "foreach" loop.
The best way to catch these errors is to have a way to show an "up-front,
in your face" message that unambiguously shows you that the suspected
condition has occurred. Even better, if you are testing your code in Debug
mode, you probably want to have the code immediately break right into
the debugger at or near the line of code where the error occurred. And
if your code is NOT running in Debug mode, you want these conditions to
have no effect, and if possible, not even to have any compiled code to
bloat your assembly at all, without you having to unwire or comment -
out blocks of "test code" just to make a RELEASE build.
Conditional compilation constants with #DEFINE statements and #IF ---
etc. statements, long used by C++ programmers, are also available in managed
code. However, there is an even easier and much more elegant way to wire
up code with such conditional statements or methods- the Conditional
attribute:
[Conditional ( "DEBUG" )]
private void DebugOnly()
{
lblMessage.Text+="<br/><font color=red>This conditional
method is called on DEBUG only!</font>";
}
Yes, the above method will ONLY be available when you run your ASP.NET
project in DEBUG mode. It is ignored by the compiler in RELEASE mode.
And, so are any in-code calls to the method - they are simply ignored
by the compiler on a RELEASE build!
Of course, you are free to declare your own conditional attributes. If
the above attribute was {Conditional("HAMBURGER")], and I had
a #DEFINE HAMBURGER statement at the top of my class file, it would work
fine just the same. The built-in defaults are, of course, DEBUG and RELEASE.
The Assert method is preferable to popping up a MessageBox or other efforts
because it brings up a dialog that offers to run the debugger, and also
provides a stack trace right out of the box with no effort on your part:

Using Asserts with ASP.NET
But, there is one big problem. . . Have you ever tried to do
a Debug.Assert in ASP.NET? Doesn't work, does it? No, because ASP.NET
doesn't support Winforms - based dialog boxes. The reason for this should
be obvious - 99% of the time, there is nobody logged on to the webserver
machine to see the dialog and dismiss it. In addition, you probably want
this action to occur ONLY when it's on your local machine that you are
developing on, and NEVER when the application is called from a remote
machine at all, even in DEBUG mode.
As luck would have it, the developers of the ASP.NET runtime saw fit
to make many of the Base classes and methods overridable and customizable.
The Debug and the Trace classes can both be customized to provide custom
functionality. These classes both delegate to System.Diagnostics.TraceListener.
So by simply creating our own AssertHandler class, deriving from TraceListener
and overriding the default methods, we can indeed create an elegant and
useful way to get exactly what we want in ASP.NET - an Assert Dialog that
pops up only when an Assert statement is fired, complete with the Stack
Trace and the optional button to break into the debugger. On top of that,
we can easily wire it up so that it only happens when we are debugging
the project on our LOCAL MACHINE.
The Framework comes with some predefined TraceListeners that you can
add to the Listeners array for additional outputs. The first is the EventLogTraceListener
class, which will send output to the specified event log. The second is
TextWriterTraceListener, which directs output to a TextWriter or Stream,
such as the Console.Out function of FileStream. For example, if you wanted
to add a TextWriterTraceListener to the chain:
Debug.Listeners.Add(new TextWriterTraceListener
( "Trace.Log" ) );
What I've chosen to do here is implement a small class, "AssertHandler.Assert",
that overrides the default methods in the base class to accomplish what
we want to do. In this manner, we only need to add one TraceListener,
and it will handle all the cases:
using System;
using System.Web;
using System.Diagnostics;
using System.Windows.Forms;
namespace AssertHandler
{
public class Assert : DefaultTraceListener
{
public override void Fail(string message)
{
// plain deal with no override specialties
Fail(message,null);
}
public override void Write(string message1)
{
// same here
}
public override void Fail(string message1, string
message2)
{
HttpRequest req=HttpContext.Current.Request;
if(req==null || req.UserHostName==req.ServerVariables.Get("LOCAL_ADDR"))
{
StackTrace t = new StackTrace();
string s = t.ToString();
// if they put "PAGE" at the beginning of the second message,
// write trace directly to page instead of dialog with debugger
trigger
if(message2.Substring(0,4)=="PAGE")
{
HttpContext.Current.Trace.IsEnabled=true;
HttpContext.Current.Trace.Write(message1 + "\r\n" +message2
+ "\r\n" + s);
return;
}
if(message2.Substring(0,3)=="LOG")
{
// if they put "LOG" at the beginning
of the second message,
// write trace to Event Log entry instead of dialog with debugger
trigger , showing the Page name
System.Diagnostics.EventLog.WriteEntry(
HttpContext.Current.Request.ServerVariables["SCRIPT_NAME"].ToString(),
message1 +"\r\n" +message2 +
"\r\n" +s);
return;
}
string strDisplay =message1 + "\n"
+ message2 + "\n" + "Launch Debugger?\n\n" +
t.ToString();
DialogResult r= MessageBox.Show(strDisplay, "Assertion failed.",MessageBoxButtons.YesNo,MessageBoxIcon.Error
,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);
if(r==DialogResult.Yes)
Debugger.Break();
}
}
public Assert()
{
}
}
}
|
You can add this to your Global class as follows:
protected void Application_Start(Object sender,
EventArgs e)
{
Debug.Listeners.Clear();
Debug.Listeners.Add(new AssertHandler.Assert());
}
-- and you are all wired up to go!
Now you have three options:
First, you can do a Debug dialog:
private void Button1_Click(object sender, System.EventArgs
e)
{
Session["ctr"]=(int)Session["ctr"] +1;
Debug.Assert((int)Session["ctr"] % 3!=0,"Assert Fired!","Counter="
+ Session["ctr"].ToString());
}
Second, you could do a Trace to the page:
private void Button2_Click(object sender, System.EventArgs
e)
{
Session["ctr"]=(int)Session["ctr"] +1;
Debug.Assert((int)Session["ctr"] %3!=0,"Assert Fired!","PAGE:Counter="
+ Session["ctr"].ToString());
}
Finally, you could choose only to write the information to the event
log:
private void Button3_Click(object sender, System.EventArgs
e)
{
Session["ctr"]=(int)Session["ctr"] +1;
Debug.Assert((int)Session["ctr"] %3!=0,"Assert Fired!","LOG:Counter="
+ Session["ctr"].ToString());
}
The downloadable solution illustrates possible usage for all three methods,
and I am sure you can think of more! Just right-click on the folder you
unzip it into, choose Sharing and Web Sharing, share the folder to make
an IIS virtual directory, and you are ready to load and run the solution
out of the box.
Download
the code that accompanies this article
Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform. Pete's samples at GotDotNet.com have been downloaded over 41,000 times. You can read Peter's UnBlog Here. --><-- NOTE: Post QUESTIONS on FORUMS! |  |
Do you have a question or comment about this article? Have a programming problem you need to solve? Post it at eggheadcafe.com forums and receive immediate email notification of responses.
|