Improving your code quality through documentation

I believe that it’s readily possible to improve the quality of your code by documenting it as you go along. It’s a given that in order to create any documentation at all in the .NET environment that developers have to add XML comments, we should all be familiar with how to add them so I’m only going to point you in the right direction of you don’t already do this: http://msdn.microsoft.com/en-us/magazine/dd722812.aspx

Although adding comments is the first step you really shouldn’t stop there. Those comments can be easily and automatically turned into documentation with every build. Third party tools that turn your XML comments into nice looking and formatted documentation that also integrate with Visual Studio are readily available.

Personally I’m using Sandcastle Help File Builder (SHFB) as it’s well established, builds Microsoft lookalike documentation, and, most importantly, is free so there’s no need to justify any expense with the boss.

The advantage in using a tool is that you can then make sure that your documentation is developed along with your code. Which has its own benefit of improving the coverage and quality of your documentation and also has a secondary effect on improving the quality of your code as you not only need to think about getting the code working but also about understanding and communicating its purpose to others. Thus documenting your code becomes a way of reviewing and improving it. The mistakes you catch during the development phase saves your time threefold as compared to fixing it during testing or after release.

If you’re not using the documentation tools as you go along then not only is the documentation less likely to be created in the first place but it can be seen as a last minute chore and thus less effort is put into it and as a result the quality is not as good.

Anyway, enough justification, you’re already with me or you’ve stopped reading at this point. Here’s how to add ongoing documentation with Sandcastle Help File Builder (SHFB).

First, install SHFB from here: http://shfb.codeplex.com/. The installation instructions are here: http://www.ewoodruff.us/shfbdocs/Index.aspx?topic=html/8c0c97d0-c968-4c15-9fe9-e8f3a443c50a.htm you should use the ‘Guided Installation’ as it’s very easy. This will include a ‘Visual Studio Integration Package’ that allows you to create and build the documentation project within Visual Studio. However there is also a Sandcastle GUI for developing projects separately.

Once it’s all installed open up Visual Studio and your development project. Add a new project, select the newly added Documentation category, select ‘Sandcastle Help file builder Project, set a name and location and click OK.

Sandcastle works be reading the compiled DLL/EXE, extracting the XML Comments, and applying templates to turn them into documentation. This does increase the total build time, especially the first time you build the help project. The Documentation Sources folder in the help project lists which DLLs/EXEs are read. You add a DLL/EXE by right clicking the Documentation Sources folder and selecting Add Documentation Source, browse to your build folder, select your DLL/EXE and click OK. Now try a build.

By default SHFB is set to create a CHM documentation file, this will be created in a Help folder under the project folder but like a bin folder it is not included as part of the project. To see your documentation click on the Show All Files button, navigate to the .chm file and double click on it.

Marvelous isn’t it, you just saved yourself weeks of fiddling about with Word. If you’re smart you made sure to estimate the documentation phase as at least three weeks long so now you’ve got plenty of time to jet of to the Bahamas and indulge your Bond fantasies as you ‘work from home’.

Copy properties from base class to inherited class in copy constructor

When you want to inherit the properties of a base class in a derived class it’s handy to have a copy constructor that takes an instance of the base class as a parameter and copies the properties from the base class into the new class.

Usually you have to write a long list of source.property = me.property statements which is both tedious, typo prone, and must be rewritten every time either class changes.

Wouldn’t it be better if there was a single function you could call to do the bulk of the work for you?

Imports System.Reflection
''' <summary>
''' Library of copying functions
''' </summary>
''' <remarks>
''' Reference: http://stackoverflow.com/questions/1198886/c-sharp-using-reflection-to-copy-base-class-properties
''' </remarks>
Public NotInheritable Class Copier
''' <summary>
''' Generically copies the properties of one object to another.
''' </summary>
''' <typeparam name="T1">Source type</typeparam>
''' <typeparam name="T2">Target type</typeparam>
''' <param name="source">Source object</param>
''' <param name="target"></param>
''' <returns>Source object</returns>
''' <remarks></remarks>
Public Shared Function CopyFrom(Of T1 As Class, T2 As Class)(source As T1, target As T2) As T1

'Get the properties from each object
Dim srcFields As PropertyInfo() = target.[GetType]().GetProperties(BindingFlags.Instance Or BindingFlags.[Public] Or BindingFlags.GetProperty)
Dim destFields As PropertyInfo() = source.[GetType]().GetProperties(BindingFlags.Instance Or BindingFlags.[Public] Or BindingFlags.SetProperty)
'Copy all matches
For Each [property] In srcFields
Dim dest = destFields.FirstOrDefault(Function(x) x.Name = [property].Name)
If dest IsNot Nothing AndAlso dest.CanWrite Then
dest.SetValue(source, [property].GetValue(target, Nothing), Nothing)
End If
Next

Return source

End Function

#Region "Example Usage"
''' <summary>
''' Example base class with a single property.
''' </summary>
''' <remarks></remarks>
Private Class SelfCopyingBase

Public Property Data As String

End Class
''' <summary>
''' Example derived class.
''' </summary>
''' <remarks></remarks>
Private Class SelfCopying
Inherits SelfCopyingBase
''' <summary>
''' Copy constructor.
''' </summary>
''' <param name="source"></param>
''' <remarks></remarks>
Public Sub New(source As SelfCopyingBase)

Copier.CopyFrom(Of SelfCopyingBase, SelfCopying)(source, Me)

End Sub

End Class

#End Region

End Class

Enable mapped drive browsing for Visual Studio

To enable mapped drive browsing for Visual Studio add a new key of type DWORD named EnableLinkedConnections with a value of 1 to the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System key.

Securely Share a Drive on Your Hyper-V Guest Machine

Start your Remote Desktop Connection, go to the Local Resources tab and click the More button.

Local Resources tab

Local Resources tab

Expand the Drives branch and select any local or networked drives you want to use.

Local Devices and Resources

Local Devices and Resources

Connect and open My Computer/File Explorer and you will find the shared drives.

Shared Drives

Shared Drives

All done and much simpler and much more secure than sharing a folder on your PC with ‘Everyone’ and then connecting it as a mapped drive on the virtual box.

Test a database connection

  1. Create a file called [SOMETHING].udl
  2. Double click to run it
  3. Enter connection details in the dialog
  4. Click ‘Test Connection’

The go here: http://support.microsoft.com/kb/328306

Self Serializing Base Class

This class can be used as a base for any class that needs to serialize and deserialize itself.

Imports System.Xml.Serialization
Imports System.Xml
Imports System.IO
''' <summary>
''' Base class for any object that can serialize itself either normally or cleanly. i.e. without the xml declaration thus making it useful for insertion into other xml.
''' </summary>
''' <remarks>Note that self deserialization is not possible</remarks>
Public MustInherit Class SelfSerializer
''' <summary>
''' This class can serialize itself with namespace and xml declaration.
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
<System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")>
Public Function Serialize() As String
Dim serializer As New XmlSerializer(Me.GetType)
'Need to serialize without namespaces to keep it clean and tidy
Dim emptyNS As New XmlSerializerNamespaces({XmlQualifiedName.Empty})
Using stream As New StringWriter(), writer As XmlWriter = XmlWriter.Create(stream)
'Serialize the item to the stream using the namespace supplied
serializer.Serialize(writer, Me)
'Read the stream and return it as a string
Return stream.ToString
End Using
End Function
''' <summary>
''' This class can serialize itself without namespace or xml declaration.
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
<System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")>
Public Function SerializeAsElement() As String
Dim serializer As New XmlSerializer(Me.GetType)
'Need to serialize without namespaces to keep it clean and tidy
Dim emptyNS As New XmlSerializerNamespaces({XmlQualifiedName.Empty})
'Need to remove xml declaration as we will use this as part of a larger xml file
Dim settings As New XmlWriterSettings()
settings.OmitXmlDeclaration = True
settings.NewLineHandling = NewLineHandling.Entitize
settings.Indent = True
settings.IndentChars = (ControlChars.Tab)
Using stream As New StringWriter(), writer As XmlWriter = XmlWriter.Create(stream, settings)
'Serialize the item to the stream using the namespace supplied
serializer.Serialize(writer, Me, emptyNS)
'Read the stream and return it as a string
Return stream.ToString
End Using
End Function

#Region "Self deserialization"

Private myState As Object = Me
''' <summary>
''' Deserialize
''' </summary>
''' <param name="xml"></param>
''' <remarks>This is a shallow copy so won't read any nested objects.</remarks>
Public Sub Deserialize(xml As String)
Dim newMe As Object 'We don't know our own type so we have to use an object here
'Read text
Dim newSerializer As New XmlSerializer(Me.GetType)
Using newReader As New StringReader(xml)
newMe = newSerializer.Deserialize(newReader)
myState = newMe
End Using
For Each propinfo In myState.GetType.GetProperties()
Dim name = propinfo.Name
Dim realProp = (From p In Me.GetType.GetProperties
Where p.Name = name And p.MemberType = Reflection.MemberTypes.Property).Take(1)(0)
realProp.SetValue(Me, propinfo.GetValue(myState, Nothing), Nothing)
Next
End Sub

#End Region
End Class

Get the Current Directory

I hate trying to remember this so here are various options.

If you are writing an application:

Return Application.StartupPath

'or

Dim uri As String = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)
Dim local As String = New Uri(uri).LocalPath
Return local

If you are writing a class library:

Return My.Application.Info.DirectoryPath

If you are writing an ASPX page:

Dim serverfilepath As String = Server.MapPath(filepath)

Include a namespace prefix when serializing or deserializing

In order to include a namespace prefix when serializing a class we must add an xmlns property of type XmlNamespaceDeclaration to the class and then add a prefix in the class constructor.

Imports System.Xml.Serialization

    <Serializable(), XmlRoot("navigation", Namespace:="<a href="http://www.tla.com/">http://www.tla.com</a>")>
    Public Class Navigation
        ''' <summary>
        ''' Add a namespace declaration so that we can use a prefix for this class.
        ''' </summary>
        ''' <remarks>
        ''' See <a href="http://msdn.microsoft.com/en-us/library/vstudio/system.xml.serialization.xmlnamespacedeclarationsattribute(v=vs.90).aspx">http://msdn.microsoft.com/en-us/library/vstudio/system.xml.serialization.xmlnamespacedeclarationsattribute(v=vs.90).aspx</a>
        ''' and <a href="http://stackoverflow.com/questions/1254544/how-do-i-specify-xml-serialization-attributes-to-support-namespace-prefixes-duri">http://stackoverflow.com/questions/1254544/how-do-i-specify-xml-serialization-attributes-to-support-namespace-prefixes-duri</a> </remarks>
        <XmlNamespaceDeclarations> Public xmlns As New XmlSerializerNamespaces

        Public Sub New()
            'Add thenamespace prefix
            xmlns.Add("tla", "<a href="http://www.tla.com/">http://www.tla.com</a>")
        End Sub

    End Class

    <XmlType("heading")>
    Public Class Heading

        Public Sub New()
   'Default constructor
        End Sub

        Private myQuestions As List(Of Question)
        ''' <summary>
        ''' Flat sequence of questions.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>Using the XmlElementAttribute markup renders the list as a flat sequence.</remarks>
        <XmlElementAttribute("question", GetType(Question))>
        Public Property Questions() As List(Of Question)
            Get
                If myQuestions Is Nothing Then myQuestions = New List(Of Question) 'Lazy loading
                Return Me.myQuestions
            End Get
            Set(value As List(Of Question))
                Me.myQuestions = value
            End Set
        End Property
  
    End Class

    <XmlType("question")>
    Public Class Question
 
        <XmlAttribute()> Public Property id() As String
  
    End Class

 

 

Get the application directory

Dim path As String 
path = System.IO.Path.GetDirectoryName( _
   System.Reflection.Assembly.GetExecutingAssembly().Location)

Utilising a .NET DLL with a .config file in VB6

This isn’t great practice but it’s sometimes necessary to use a config file for a DLL developed in .NET that then needs to be called from VB6.

To debug a VB6 app/exe that uses a .NET dll with a .config file you must create a vb6.exe.config file in the same folder as the VB6 executable. Typically this is C:\Program Files\Microsoft Visual Studio\VB98.

When the VB6 dll/exe is compiled the config file must be named the same as the dll/exe with a .config extension and be placed in the same location as the dll/exe. e.g. MyVB6Program.exe.config

The .NET DLL can use code like this to retrieve, for instance, connection strings:

'Fetch the connection string from the app.config file
Dim settings As ConnectionStringSettings = ConfigurationManager.ConnectionStrings(name)
If settings IsNot Nothing Then
' Retrieve the partial connection string.
Dim connectString As String = settings.ConnectionString
' Create a new SqlConnectionStringBuilder based on the partial connection string retrieved from the config file.
Dim builder As DbConnectionStringBuilder
'We need to act differently depending on wether we are using Access or SQL
If settings.ProviderName.ToUpper = "SYSTEM.DATA.ODBC" Then
builder = New OdbcConnectionStringBuilder(connectString)
' Supply the additional values.
builder.Add("Uid", "USER_NAME_HERE")
builder.Add("Pwd", "PASSWORD_HERE")
'This is a bit of a hack but we can check the odbc connection string to see if it contains the phrase 'SQL Server'
'as all sql server odbc connection strings up to and including version 11 will contain this phrase
If settings.ConnectionString.IndexOf("SQL Server", 0, StringComparison.CurrentCultureIgnoreCase) &lt;&gt; -1 Then
useSQLServer = True 'Set the useSQLServer flag
Else
Throw New Exception("Provider '" &amp; settings.ProviderName &amp; "' is not allowed.")
End If
Return builder.ConnectionString
Else
Throw New Exception("Unable to retrieve app.config connectionstrings.")
End If