Introduction
Hi to everybody. I am currently developing an in-house n-tier application, and has managed to reduce vast amounts of code through Reflection. The one part that is still left to manual, tedious code entry is in the client side business objects. On the smaller objects, this isn't a real issue, but some of these objects have 100+ public read/write properties - and of course, the private field declarations to store the data. The real objection I had to this was the repetitive nature of building the properties and including some basic validation and error checking.
Using the code
The example has been simplified for easy reading, but this can be expanded and moved to different layers, depending on your needs. I have included a BusinessRules assembly, which contains a number of custom attributes. These attributes further simplify the validation process, but attributes is the topic of another article.
The properties - Structures.vb
We'll start with a structTEST
structure. This will serve as the variables and properties for the business object, and will also be validated.
Option Strict Off
Option Explicit On
Imports BusinessRules.Attributes
Namespace Structures
Public Structure structTEST
<NotNull(), NotEmpty(), MaxLength(50)> Public Name As String
End Structure
End Namespace
NB: I always place my structures in a Structures
namespace to keep the application ordered and logical. These structures are usually saved in the same module as the interface declaration for the server side business objects and/or DAL. They could be in a Structures project, or all stored together in a Structure code module. The choice is yours.
The validator - CProps.vb
This class does all the work... This will perform a Get/Set
on the properties, perform the validation, and throw any errors/exceptions.
Option Strict Off
Option Explicit On
Imports PropsVal.Structures
Imports BusinessRules.Attributes
Imports BusinessRules.Errors
Imports System.Reflection
Here, we set the obligatory stuff and import the appropriate namespaces. Note that we are importing the BusinessRules.Attributes
and BusinessRules.Errors
namespaces as well as System.Reflection
.
Public Class CProps
#Region " Private Attributes "
Private mVTProps As ValueType
Private lType As Type
#End Region
Now, we define the class name and set about declaring some module variables. Note that the mVTProps
object is declared as a ValueType
.
#Region " Private Properties "
Private ReadOnly Property VTType() As Type
Get
Try
If lType Is Nothing Then
lType = mVTProps.GetType
End If
Return lType
Catch ex As Exception
Throw ex
End Try
End Get
End Property
#End Region
Now, we define a private property that will be used later on. The role of this property is to return a System.Type
to the methods within, as they need it (below).
#Region " Constructors "
Public Sub New(ByVal typeName As String)
Try
Dim t As Type = Type.GetType(typeName)
If t Is Nothing Then
Throw New Exception("Type could not be loaded.")
Return
End If
mVTProps = Activator.CreateInstance(t)
Catch ex As Exception
Throw ex
End Try
End Sub
#End Region
And on we go... The constructor is defined. Here, we initialise the module variables (above) by using the typeName
(String
) parameter.
#Region " Public Methods "
Public Sub SetValue(ByVal propName As String, ByVal val As Object)
Try
Dim lObjFI As FieldInfo = VTType.GetField(propName)
lObjFI.SetValue(mVTProps, val)
BusinessRules.Validate.Validation.ValidateAndThrow(mVTProps, propName)
Catch ex As Exception
Throw ex
End Try
End Sub
And, on to the workers. As with all read/write properties, there is a Set
block. This is the Set
block. Now, remember above where we defined the mVTProps
variable as a ValueType
? This is because a structure is a value type. The FieldInfo.SetValue
only works on Object
types (any one who is familiar with this, please feel free to elaborate on this one).
Public Function GetValue(ByVal propName As String)
Try
Dim lObjFI As FieldInfo = VTType.GetField(propName)
Return lObjFI.GetValue(mVTProps)
Catch ex As Exception
Throw ex
End Try
End Function
Public Function GetAllData() As ValueType
Return mVTProps
End Function
#End Region
End Class
...and this is the Get
block and the end of the class.
The business object
The business object is what the user interface will be communicating with.
Option Strict Off
Option Explicit On
Imports PropsVal.Structures
Imports System.Reflection
Public Class BizO
#Region " Public Properties "
Public PROPS As CProps
#End Region
Again, we set our options and import our namespaces; define the class and the public properties. The PROPS
declaration is where you would typically declare all your variables (string, int, GUID, etc.), and then you would have all your property declarations with any/all validation and error checking code.
#Region " Constructors "
Public Sub New()
PROPS = New CProps(GetType(Structures.structTEST).FullName)
End Sub
#End Region
#Region " Public Methods "
Public Sub Load()
End Sub
#End Region
End Class
The constructor is where the PROPS
object is initialised. This is the critical element of this solution as the types must be correct, but even that isn't too hard. Remember the structure we created earlier? This is where we get to actually use it. The trick to making this all work is to parse the Type FullName
of the structure.
Here, I have purposely left the Load
method empty, but as per the code comments, you can see what can be done.
The UI - form1.vb
Yes, yes. I know it's a lazy name. The only code you will need to add is for the button.
Dim o As BizO
o = New BizO
Try
o.PROPS.SetValue("Name", txtName.Text)
lblErrMsg.Text = "Input tested successfully!!"
pnlStatus.BackColor = Color.Green
Catch ex As Exception
lblErrMsg.Text = ex.Message
pnlStatus.BackColor = Color.Red
End Try
lblResult.Text = "PROPS.GetValue: " + o.PROPS.GetValue("Name")
Dim x As Structures.structTEST
x = o.PROPS.GetAllData
lblResult.Text += vbCrLf + vbCrLf + "Local structure: " + x.Name
OK. Last piece... Here, we declare and instantiate a new business object.
Then, we try setting the Name
property to the value of the textbox Text
property. If there are no errors/exceptions, we display a success message and set the panel to green. Any exceptions result in the error message being displayed and the panel changing to red.
Then, purely for testing purposes, while I was building this, we retrieve the value of the Name
property and display it on screen, and finally we declare a new instance of the structTEST
structure and fill it with the contents/values of the PROPS
object, and display the value of the Name
member.
Points of interest
- The above code
has been was originally developed on the v1.1 framework. Having not used v2.0 yet, I can only assume that the above would work, but don't quote me. After porting this across to a VS2005 (Framework 2.0) solution and subsequently VS2008, no issues have occurred, and this model is in use and is being used more every day.
- The
structTEST
structure is a value type, and using FieldInfo.SetValue
only works on object types. Luckily, structures convert to a value type object easily.
Option Strict
is set to Off
only because casting a structure as a ValueType
object without a CType
fails, and I find too many other implicit type conversions fail when strict typing is set.
- The
GetAllData
method can be used to parse the contents/values to a database, DAL, or remote object as the object being returned is the structTEST
structure. The application I'm developing parses these structures to a remotable business object (via an interface), where it is checked and validated one final time. The structure is then parsed to the DAL, where the structure is used to fill stored procedure parameters (using the member names and further custom attributes) and parse it to the SQL database.
- I would have liked to make use of IntelliSense for the property names as compared with a
String
literal, but I fear the additional load may negate any performance and/or simplicity. Any ideas?
- I'm sure there are a number of items that could be done better, and I am more than happy to hear from you about them. After all, we're all hear to learn something.
Conclusion
These days, everybody has to do more with less help, and it's always due yesterday. Hopefully, this example may help you reduce the tedium, and let you get on with the really cool stuff. If I feel inspired again, I may submit some further articles on the various techniques I've found, created, and used to save time and code.