Although I've argued before that Typed DataSets aren't evil, there's one thing that bothers me a lot when dealing with them in a library project: the code that gets generated doesn't contain any XML comments. In itself, that isn't so bad (after all, which useful comments could be generated from an XSD schema?), but if the project is configured to emit an XML documentation file, you get a gazillion warnings for each member that lacks a comment. I like to keep my build warning-free to avoid "known harmless warnings" from hiding "new and important warnings" (if I can first get my stuff to build without errors, at least). You could go about and put comments on the generated file manually, but any change on the dataset's schema file will trigger the code to be regenerated so you've lost all your hard work. Hence I created the following quick and dirty solution: a Visual Studio macro that runs over all members and puts a placeholder comment (containing just the member's name) on top. Anytime the code gets regenerated, just run the macro and you're set. I'm sure this should be able to run as a post-build step or something too, but if you're lazy like me (and you're a programmer too, so you're lazy by definition), you'll already be happy you got this done.
Oh, and if you're really lazy, you can use this macro to document all your members automatically - although I'd add some random word-generator to make it look just a little more convincing (to fool the QA-guys at least).
So here's the source (with special thanks to the MSDN article on XML Comment Macros):
Imports EnvDTE
Imports System.Diagnostics
Imports System
Imports System.Xml
Imports System.IO
Imports System.Collections
Imports System.Text
Imports System.Text.RegularExpressions
Public Module XmlDocumenter
Public Sub InsertGenericSummaries()
SetCommentsRecursively(DTE.ActiveDocument.ProjectItem.FileCodeModel.CodeElements)
End Sub
Private Sub SetCommentsRecursively(ByVal elements As CodeElements)
For Each element As CodeElement In elements
' Get a comment block.
Dim doc As XmlDocument = GetCommentXml(element)
Dim summaryTag As XmlElement = doc.SelectSingleNode("/doc/summary")
If summaryTag Is Nothing Then
' Create <summary> tag
summaryTag = doc.CreateElement("summary")
summaryTag.InnerXml = element.Name
doc.FirstChild.PrependChild(summaryTag)
End If
SetCommentXml(element, doc)
' Recurse over members.
Try
SetCommentsRecursively(element.Members)
Catch ex As Exception
' Ignore exceptions (quick and dirty hack to skip elements that have no Members property)
End Try
Next
End Sub
' Code borrowed from http://msdn.microsoft.com/msdnmag/issues/05/07/XMLComments/default.aspx
Private Sub SetCommentXml(ByVal element As CodeElement, ByVal doc As XmlDocument)
Dim sb As StringBuilder = New StringBuilder
Dim sw As StringWriter = New StringWriter(sb)
Dim xw As XmlTextWriter = New XmlTextWriter(sw)
Try
xw.Indentation = 1
xw.IndentChar = Char.Parse(vbTab)
xw.Formatting = Formatting.Indented
Dim rootNode As XmlNode
If element.Language = CodeModelLanguageConstants.vsCMLanguageVB Then
rootNode = doc.FirstChild
Else ' CSharp
rootNode = doc
End If
rootNode.WriteContentTo(xw)
Try
element.DocComment = sb.ToString()
Catch ex As Exception
' Ignore
End Try
Finally
xw.Close()
sw.Close()
End Try
End Sub
' Code borrowed from http://msdn.microsoft.com/msdnmag/issues/05/07/XMLComments/default.aspx
Private Function GetCommentXml(ByVal element As CodeElement) As XmlDocument
Dim doc As XmlDocument = New XmlDocument
Dim xmlStr As String = element.DocComment
If Not xmlStr Is Nothing AndAlso xmlStr.Trim() <> String.Empty Then
If xmlStr.StartsWith("<doc>") = False Then
' Ensure single root node
xmlStr = "<doc>" & xmlStr + "</doc>"
End If
xmlStr = Regex.Replace(xmlStr, "^(\s*<doc>\s*){2,}", "<doc>")
xmlStr = Regex.Replace(xmlStr, "(\s*<\/doc>\s*){2,}$", "</doc>")
doc.LoadXml(xmlStr)
Else
Dim rootTag = doc.CreateElement("doc")
doc.AppendChild(rootTag)
End If
Return doc
End Function
End Module
Easy, effective, and ugly. Use at your own risk, it might eat your lunch sandwich.