The Methods region:
The class implements seven methods. We will go through them now.
Private Clear method:
| Private Sub Clear() |
_ObjectVersion = "0.0"
_ObjectCulture = "Unknown"
_MethodAction = "Unknown"
_UserName = System.Environment.UserName
_MachineName = System.Environment.MachineName
_OSVersion = System.Environment.OSVersion.ToString
bVersion = False
bCulture = False
The Clear method is Private because you don't want developers working with your class to wipe out the exception attributes. The method is referenced by the constructors at the time they are initializing the class. If you add new Private members to the class you should also initialize them here. You may think this Clear method is redundant because private member are already initialized when you declare them. However including their initialization here will make sense and future developers taking over the responsibility of maintaining this code should understand it easier.
There is a reference to the mystery method GetApplicationAttributes(), we will get to it very shortly.
The GetObjectData Method:
This method is the one de-serializing this class. Due to its function it is a very important one. If you are not careful coding it, the class may have serialization problems. The main goal when de-serializing and serializing objects is their private members values do not change or get lost. In other words, its state should not change.
| Public Overrides Sub GetObjectData(ByVal info As System.Runtime.Serialization.SerializationInfo, _ |
ByVal context As System.Runtime.Serialization.StreamingContext)
The method is public and overrides the base class GetObjectData method, and then it de-serializes the base class with this line of code
The info object contains the de-serialized base class. Now you should add all your new private members to the info object and you will be done.
If you are expanding this class by adding new properties, you should include their private member in this method as well.
Private method GetApplicationAttributes:
If you are familiar with the Gang of Four (GoF) design patterns, you may think this class implements a Facade Design Pattern for the application exception class. The GoF quote below is taken from their literature.
GoF Intent for Facade: Provide a unified interface to a set of interfaces in a subsystem. The Facade defines a higher-level interface that makes the subsystem easier to use.
The GetApplicationAttributes method enforces this approach; it drills deep into the exception objects trees, finding attributes later exposed as properties by the class.
| Private Sub GetApplicationAttributes() |
If Not Me.TargetSite Is Nothing Then
If Not Me.TargetSite.ReflectedType Is Nothing Then
If Not Me.TargetSite.ReflectedType.AssemblyQualifiedName Is Nothing Then
Dim s() As String = Split(Me.TargetSite.ReflectedType.AssemblyQualifiedName)
Dim s1 As String
For Each s1 In s
If bVersion = False Then
If s1.IndexOf("Version", 0) >= 0 Then
Dim s2() As String = Split(s1, "=")
_ObjectVersion = s2(1)
If _ObjectVersion.IndexOf(",") >= 0 Then
_ObjectVersion = _ObjectVersion.Substring(0, _ObjectVersion.IndexOf(","))
bVersion = True
If bCulture = False Then
If s1.IndexOf("Culture", 0) >= 0 Then
Dim s2() As String = Split(s1, "=")
_ObjectCulture = s2(1)
If _ObjectCulture.IndexOf(",") >= 0 Then
_ObjectCulture = _ObjectCulture.Substring(0, _ObjectCulture.IndexOf(","))
bCulture = True
If bVersion = True And bCulture = True Then
_ErrorDate = Now.Date
_ErrorTime = Now.TimeOfDay
The method is working with the AssemblyQualifiedName property found at the ReflectedType member, which is located inside the exception's TargetSite. All the Is Nothing validations are required by the way exception objects behave - more on that later on, in the post explaining How to use this ErrorHandlingException class.
The AssemblyQualifiedName property contains the executable's version number and its culture. Finding and exposing them will make developers' life working with this class easier.
If the implementation approach used to build this class is clear to you, you may want to use this method to find and expose additional information about the exception being handled.
The Public Save() method:
Now that this ApplicationExecption class collects a lot of information about the exception being handled, the Save() method allows developers implementing it to "save" the exception details to an error log object. The class already implements two of these error log objects; a flat text file or an Access Database; you can extend this class to implement "additional" error log objects.
| Public Sub Save(ByVal TargetErrorLog As TargetErrorLogs, _ |
ByVal TargetOutput As String)
' Save exception details to log table in ApplicationException database
If bVersion = False OrElse bCulture = False Then
Select Case TargetErrorLog
It takes two parameters:
TargetErrorLog: This is of a TargetErrorLogs type, telling the method the procedure to follow to "save" the exception details. The type was declared at the top of the class:
| Public Enum TargetErrorLogs |
Currently defining two values: FlatFile and Access. Developers working with the class can only chose two target error log objects as defined by this enumeration.
If you want to extend this class to "save" the exception details to a new error log object, then you should modify this enumeration adding its associated key-word name.
TargetOutput: If the target error log object is a flat file you should pass its name thru this parameter. If the target is a database, then the connection string "key" as defined in the application config file should be passed instead. If you extend this class to email the exception details then this parameter is likely to receive a distribution list of the emails recipients for the email. The logic within the Save() and its associated "SaveTo ..." methods deals with an empty TargetOutput value.
The next thing the Save method does is check for the application version and culture. If they are unknown the GetApplicationAttributes() is invoked, making sure the class state is up to date.
Finally, the Save method sorts out the target error log object to use based on the TargetErrorLog parameter received.
This time we are using a sort of GoF Factory Design Pattern. Each target error log object has a private "SaveTo ... " method, because you don't want developers working with this class to implement different "SaveTo ..." methods thru the application. Besides, sticking with the Save() factory method will make it a lot easier to switch from writing to a database error log into emailing the exceptions details. An entry in the application config file will do so without recompiling your application.