Saturday, June 30, 2007

Ah, the memories... XML documentation parse error: Whitespace is not allowed at this location. XML comment will be ignored.

Today I had a flashback to one of the very first VS05 IDE error message I ever got. Ah, the memories...

XML documentation parse error: Whitespace is not allowed at this location. XML comment will be ignored.

This design time errors occurs when you place an reserved character in your XML documentation within your source code. I commonly get this error when I use the '&' character so I just delete it and type the word 'and'. I imagine you can get this error if you use '<', '>' and a host of other characters. Those familiar with the escape characters in HTML will know to replace those characters with &amp;amp;lt; and > respectively.

I originally received this error when I was writing my very first comment so it really threw me for a loop now it is all in a day's work.

Thursday, June 28, 2007

Debug.Assert + Preserved Breakpoints = Safer Adventure

During today's adventure I stumbled over one of the many great new features in Visual Studio. I have always known that VS05 persists my breakpoints from one day to the next but today I experienced just how great this can be.

After reading chapter 3 of John Robbins' Debugging Microsoft .NET 2.0 Applications I started to implement debug.assert statements throughout my code. Knowing that debug.assert statements are great while I am developing they are useless at runtime in a release compile I decided, "Boy, everywhere that I have a debug.assert statement I need a trace statement." This way I can write trace output to a trace log in a release compiled object when debug.assert does nothing for me. I am smart enough to know better than to run around to all my Debug.Assert statements and place a Trace.Write right under it. So I wrote a separate method that accomplished both. It looked something like this:

#Const TRACE = True
Imports System.Diagnostics
Public Module DebuggingUtils
  Public Sub Assert(ByVal message As String)
    Debug.Assert(False, message)
    Const TRACE_SOURCE As String = "tsMain"
    Dim tracer As New TraceSource(TRACE_SOURCE)
    Trace.AutoFlush = True 'Trace is correct, NOT tracer
    tracer.TraceData(TraceEventType.Error, 0, message)
    tracer.Flush() 'make sure the output goes to the logs.
    tracer.Close() 'explicitly close this object.
    tracer = Nothing
  End Sub
End Module


That method worked fine but here is the cool part. I placed a breakpoint on the Debug.Assert line so now every time I get into a situation where the debug dialog box would be displayed I will hit this breakpoint instead of that nasty dialog box that scares me every time I see it. My adventures in .Net are scary enough without that nasty dialog.

Now, you might be wondering about the wisdom of having my Debug.Assert() statement pass false as the first parameter (which means that the assertion will fail every time causing the debug dialog to display) but here is my reasoning. I am trying to follow the advice of those that have travelled these parts before me (in this case John Robbins) and he recommends that I validate every parameter of every public method. So, the first thing each of my methods do is validate each parameter with 1 or many if/then statements and if that variable is invalid the assert method will be called right before the error gets thrown. Here is a sample:

Private Sub DoSomething(ByVal someNumber As Integer)
  If someNumber <= 0 Then

    DebuggingUtils.Assert("someNumber <= 0")
    Throw New ArgumentOutOfRangeException("someNumber")

  End If
...
End Sub


Additional Notes:
You will notice that the first first statement in my DebuggingUtils module is
#Const TRACE = True
I found that regardless of any trace settings in web.config all of my trace statements were ignored at runtime. I couldn't even set breakpoints on them in design time! Once I defined the TRACE compiler constant within my file everything was fine. Perhaps someone will know of a better solution but I am not too worried however because I only have trace statements within this single file so I do not have to run around and maintain this constant in each file.

Additional Resources:
There is a lot more to know about tracing. Read more about tracing by reading p.197+ in Francesco Balena's superb book Programming Microsoft Visual Basic 2005: The Language. This is one of my 2 favorite .Net books & authors. I disclosed my other favorite author in a prior post. I'll tell you the other book in a future post. That can be a little adventure for you!

Thanks for sharing this adventure with me.

Using Regex.Replace for the first time

In VB6 I got really good at using replace. But now I'm trying to avoid being replaced myself so I decided I better learn to use the infinitely more powerful replace feature within the System.Text.RegularExpressions namespace.

I had read a little about the syntax for using regular expressions for validation and I'd used the match method for validation of email addresses. Of course, I am still at the stage where I have to borrow complex regular expressions from regexlib.com but today's adventure was simple enough that I didn't need a complex pattern.

All I wanted to do was take a string like "PaymentDueDate" and convert it to "Payment Due Date". This is pretty simple so I decided it would be a great introduction. All I had to do was use the regex.replace to find each upper case character and put a space in front of each one. After reading one of Scott Mitchell's posts on 4GuysFromRolla.com I finally came across the nugget I needed to make this work.

Here is the code that worked for me:


Public Function AddSpaces(ByVal titleCasedText As String) As String
  Return Regex.Replace(titleCasedText, "([A-Z])", " $1").Trim
End Function


I knew that [A-Z] would find matches of upper cased characters but I'd never tried to use replace before so knowing what to put in as the replacement text was a mystery. Thank God for the new unit test features in .Net because that allowed me to quickly write a test and keep altering my function until I finally got it right. They always say, "Start by writing a test that will fail and then write the function that will allow it to pass." Well, today I definitely followed that rule! My first 3 or 4 attempts at writing this function were definitely failures. The key to making this work was enclosing the [A-Z] in the parentheses. This allowed me to make a "back reference" to those matches. Then I can apply formatting to each back reference by using the $N syntax. Here is an excerpt from Scott Mitchell's post:

For example to format a 10 digit raw phone number use this pattern:
(\d{3})(\d{3})(\d{4})
And use this replacement pattern that takes the 3 back references & reformats them:
($1) $2-$3

Back references take the form $N, where N specifies what back reference you're interested in - the first back reference in the pattern is accessed via $1, the second accessed via $2, and so on. To specify that a particular portion of the pattern can be used as a back reference, simply surround that portion with parentheses in the pattern.

So I learned 2 things in this adventure.
1) How to use System.Text.RegularExpressions.regex.replace()
2) Whenever I do a Google search on a .Net topic I should always include "Scott Mitchell" in the search criteria and then I'll let Google's regular expressions handle it from there. For those of you that are .Newbies like me and have to search the Internet everyday you will soon learn that given 2 search results always click on the one written by Scott Mitchell.

In our next adventure... I will learn the best way to write this function so I don't need the .Trim at the end. That was my kluge for not knowing (yet) how to skip the first character in the input string.

Thanks for joining me on my first adventure.