Tag Archives: VB.NET

VB.NET Equivalent of C# typeof

This is one of the tricky bits of C# to VB.NET conversion that I, like others, always forget. Anyway,

typeof(thing)

should be converted to

GetType(thing)

String Enums and Constants

The ever occuring problem of how to implement a String Enum or a Constant in an elegant way is an annoyance for all programmers. No programmer likes hardcoding constants or enums but they are often necessary. Below is another way to accomplish this.


Imports System.ComponentModel
Imports MyPatterns.TypeCodes
Imports MyPatterns.CategoryCodes

Public Enum TypeCodes
    <Description("Type A")> TypeA = 0
    <Description("Type B")> TypeB
    <Description("Type C")> TypeC
End Enum

Public Enum CategoryCodes
    <Description("General")> General = 0
    <Description("Specific")> Specific
    <Description("Other")> Other
End Enum

Public NotInheritable Class Constants

#Region "Resources"

    Private Shared myTypes As Dictionary(Of TypeCodes, ConstantItem) = Nothing

    Public Shared ReadOnly Property Types() As Dictionary(Of TypeCodes, ConstantItem)
        Get
            If myTypes Is Nothing Then
                myTypes = New Dictionary(Of TypeCodes, ConstantItem)
                BuildTypes(myTypes)
            End If
            Return myTypes
        End Get
    End Property

    Private Shared Sub BuildTypes(ByRef dict As Dictionary(Of TypeCodes, ConstantItem))
        With dict
            .Add(TypeCodes.TypeA, New ConstantItem(TypeCodes.TypeA.Description, "Type A are..."))
            .Add(TypeCodes.TypeB, New ConstantItem(TypeCodes.TypeB.Description, "Type B are..."))
            .Add(TypeCodes.TypeC, New ConstantItem(TypeCodes.TypeC.Description, "Type C are..."))
        End With
    End Sub

#End Region

#Region "Categories"

    Private Shared myCategories As Dictionary(Of CategoryCodes, ConstantItem) = Nothing

    Public Shared ReadOnly Property Categories() As Dictionary(Of CategoryCodes, ConstantItem)
        Get
            If myCategories Is Nothing Then
                myCategories = New Dictionary(Of CategoryCodes, ConstantItem)
                BuildCategories(myCategories)
            End If
            Return myCategories
        End Get
    End Property

    Private Shared Sub BuildCategories(ByRef dict As Dictionary(Of CategoryCodes, ConstantItem))
        With dict
            .Add(CategoryCodes.General, New ConstantItem(CategoryCodes.General.Description, "General category"))
            .Add(CategoryCodes.Specific, New ConstantItem(CategoryCodes.Specific.Description, "Specific category"))
            .Add(CategoryCodes.Other, New ConstantItem(CategoryCodes.Other.Description, "Other category"))
        End With
    End Sub

#End Region

End Class

Public NotInheritable Class ConstantItem

#Region "Constructors"
    ''' <summary>
    ''' Default constructor.
    ''' </summary>
    Public Sub New()
        'Do nothing
    End Sub
    ''' <summary>
    ''' Simple constructor.
    ''' </summary>
    Sub New(value As String)
        Me.Name = value
        Me.Description = value
    End Sub
    ''' <summary>
    ''' Proper constructor.
    ''' </summary>
    Sub New(name As String, description As String)
        Me.Name = name
        Me.Description = description
    End Sub

#End Region

    Property Name As String
    Property Description As String

    ''' <summary>
    ''' See http://stackoverflow.com/questions/293215/default-properties-in-vb-net
    ''' </summary>
    Public Shared Widening Operator CType(value As String) As ConstantItem
        Return New ConstantItem(value)
    End Operator
    ''' <summary>
    ''' See http://stackoverflow.com/questions/293215/default-properties-in-vb-net
    ''' </summary>
    Public Shared Widening Operator CType(value As ConstantItem) As String
        Return value.Name
    End Operator

End Class

Public Class ConstantsExample

    Public Sub UseConstant()

        Dim value As String = Constants.Types(TypeA)
        Dim category As String = Constants.Categories(General)

    End Sub

End Class

Which endpoint is actually being used?

Private Function GetEndpointAddress(name As String) As String
Debug.Print("--- GetEndpointAddress ---")
Dim address As String = "Unknown"
Dim appConfig As Configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
Debug.Print("app.config: " & appConfig.FilePath)
Dim serviceModel As ServiceModelSectionGroup = ServiceModelSectionGroup.GetSectionGroup(appConfig)
Dim bindings As BindingsSection = serviceModel.Bindings
Dim endpoints As ChannelEndpointElementCollection = serviceModel.Client.Endpoints
For i As Integer = 0 To endpoints.Count - 1
Dim endpoint As ChannelEndpointElement = endpoints(i)
Debug.Print("Endpoint: " & endpoint.Name & " - " & endpoint.Address.ToString)
If endpoint.Name = name Then
address = endpoint.Address.ToString
End If
Next
Debug.Print("--- GetEndpointAddress ---")
Return address
End Function

Retrieving files from application directory using Reflection

When retrieving a list of files in the directory of the executing application/DLL you can use the following code:

 Dim uri As String = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) Dim local As String = New Uri(uri).LocalPath
 Dim filenames() As String = System.IO.Directory.GetFiles(local, “*.*”)
 

CodeBase returns a URI which means that if you pass the result of GetDirectoryName directly to GetFiles a ‘URI formats are not supported’ error is thrown. Thus we need to convert the URI to a local path.

An interesting way to get around the limitations of a Using block when calling WCF Services

As you may have already experienced there are potential problems with a Using block when interacting with a WCF Service. For example this code can easily produce an undecipherable WCF CommunicationObjectFaultedException  error for no apparent reason:

Using service As New WCF_ServiceReference.MessageClient
'This is the 'payload' code block:
For Each item As Message In MessageList
If service.SendMessage(item) = True Then
MessageList.Remove(item)
End If
Next
End Using

After some research you will discover that the Microsoft recommendation is to wrap the ‘payload’ code in a Try…Catch block instead of a Using block. See ‘Expected Exceptions’ (http://msdn.microsoft.com/en-us/library/aa354510.aspx) for details. This results in the rather longwinded code below:

Dim service As New WCF_ServiceReference.MessageClient
'Exceptions that are thrown from communication methods on a Windows Communication Foundation (WCF) client are either expected or unexpected.
Try
'Here is the 'payload' code block again
For Each item As Message In MessageList
If service.SendMessage(item) = True Then
MessageList.Remove(item)
End If
Next
service.Close()
Catch timex As TimeoutException
'Expected exceptions from communication methods on a WCF client include TimeoutException, CommunicationException, and any derived class of CommunicationException. These indicate a problem during communication that can be safely handled by aborting the WCF client and reporting a communication failure. Because external factors can cause these errors in any application, correct applications must catch these exceptions and recover when they occur.
service.Abort()
Catch commex As CommunicationException
'Code that calls a client communication method must catch the TimeoutException and CommunicationException. One way to handle such errors is to abort the client and report the communication failure.
service.Abort()
Catch ex As Exception
'Typically there is no useful way to handle unexpected errors, so typically you should not catch them when calling a WCF client communication method.
'Throw
Finally
service.Dispose()
End Try

However there is another way to implement this in a manner much more similar to a Using block:

ServiceHelper.Using(Of WCF_ServiceReference.MessageClient)(
Sub(service)
'Here is the 'payload' code block again
For Each item As Message In MessageList 'Note that MessageList is in scope because it is part of the containing class
If service.SendMessage(item) = True Then
MessageList.Remove(item)
End If
Next
End Sub)

This method relies on a ServiceHelper class with a Using function:

Public Shared Sub [Using](Of ServiceT As {ICommunicationObject, IDisposable, New})(action As Action(Of ServiceT))
Dim service = New ServiceT()
Dim serviceclosed As Boolean = False
Try
action(service)
If service.State <> CommunicationState.Faulted Then
service.Close()
serviceclosed = True
End If
Finally
If serviceclosed = False Then
service.Abort()
End If
End Try
End Sub

The using function takes a generic parameter that matches the interfaces supported by a WCF service and an anonymous subroutine (aka lambda). What this means in practice is that we call ServiceHelper.Using passing a WCF service type and a subroutine that consists of the code inside the original Using block. The Using function executes the procedure and, crucially, handles any faulted errors, calling close or abort as required. This has the advantages of being considerably fewer lines of code then in the original procedure, handles errors gracefully, and can be debugged easily.

Note that the code inside the anonymous procedure must not call service.Close or service.Abort as these are handled by the Using procedure.

The only problem with the ServiceHelper class is that we can only pass a subroutine and not a function. So going further we can overload the Using procedure by adding another procedure to our ServiceHelper class to allow for a function that returns a result:

Public Shared Function [Using](Of ServiceT As {ICommunicationObject, IDisposable, New}, TResult)(action As Func(Of ServiceT, TResult)) As TResult
Dim service = New ServiceT()
Dim serviceclosed As Boolean = False
Dim result As TResult
Try
result = action(service)
If service.State <> CommunicationState.Faulted Then
service.Close()
serviceclosed = True
End If
Finally
If serviceclosed = False Then
service.Abort()
End If
End Try
Return result
End Function

Which can be used like this:

Dim result As Boolean = ServiceHelper.Using(Of WCF_ServiceReference.MessageClient, Boolean)(
Function(service) As Boolean
'Here is the 'payload' code block again
For Each item As Message In MessageList 'Note that MessageList is in scope because it is part of the containing class
If service.SendMessage(item) = True Then
MessageList.Remove(item)
End If
Next
Return True
End Function)

Naturally you can carry on adding other Using procedures with different signatures to account for all the variations of the  Action and Func delegates  that are available but the simplest solution is to add a single input parameter, which can take an object of any type, and use that to pass in any required data. This gives us two more procedures to round out our ServiceHelper class:

Public Shared Sub [Using](Of ServiceT As {ICommunicationObject, IDisposable, New}, TParameter)(input As TParameter, action As Action(Of ServiceT, TParameter))
Dim service = New ServiceT()
Dim serviceclosed As Boolean = False
Try
action(service, input)
If service.State <> CommunicationState.Faulted Then
service.Close()
serviceclosed = True
End If
Finally
If serviceclosed = False Then
service.Abort()
End If
End Try
End Sub

Public Shared Function [Using](Of ServiceT As {ICommunicationObject, IDisposable, New}, TParameter, TResult)(input As TParameter, action As Func(Of ServiceT, TParameter, TResult)) As TResult
Dim service = New ServiceT()
Dim serviceclosed As Boolean = False
Dim result As TResult
Try
result = action(service, input)
If service.State <> CommunicationState.Faulted Then
service.Close()
serviceclosed = True
End If
Finally
If serviceclosed = False Then
service.Abort()
End If
End Try
Return result
End Function

Which are used like this:

Dim messages As New List(Of Message)
ServiceHelper.Using(Of WCF_ServiceReference.MessageClient, List(Of Message))(messages,
Sub(service, msglist)
'Here is the 'payload' code block again
For Each item As Message In msglist 'Note that we pass in the message list as a parameter and thus do not rely on the containing class's property
If service.SendMessage(item) = True Then
MessageList.Remove(item)
End If
Next
End Sub)

Dim messages As New List(Of Message)
Dim result As Boolean = ServiceHelper.Using(Of WCF_ServiceReference.MessageClient, List(Of Message), Boolean)(messages,
Function(service, msglist) As Boolean
'Here is the 'payload' code block again
For Each item As Message In msglist 'Note that we pass in the message list as a parameter and thus do not rely on the containing class's property
If service.SendMessage(item) = True Then
MessageList.Remove(item)
End If
Next
Return True
End Function)

Don’t Use My.Settings

There is no benefit to using My.Settings in VB.NET development.

Firstly My.Settings is only available in the VB.NET environment so it’s confusing for anyone else not familiar with the environment and a bad habit to get into.

Secondly the XML to support it in the app.config file is verbose, unwieldy, and typo prone making it difficult to cut and paste to another app.config file.

Thirdly, if you are creating a class library the settings are compiled into the DLL meaning that they cannot be altered and may be used unexpectedly.

Using ConfigurationManager.AppSettings is much easier and the XML to support it is much smaller, simpler and easier to transfer between app.config files without error.

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

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