Introduction
The GridView
control in ASP.NET 2.0 is a very robust control, and I particularly like how we can map GridView
controls to object data sources. Furthermore, I appreciate how the GridView
enables sorting and paging with no code required. Very nice! A benefit of mapping GridView
s to object data sources (in my opinion) is that it promotes a tiered architecture. However, if you map a GridView
to an object data source, you may encounter the following exception when trying to sort a GridView
:
The data source 'objDataSource' does not support sorting
with IEnumerable data. Automatic sorting is only supported
with DataView, DataTable, and DataSet
Now, it seems a bit absurd to me to lose all the great benefits of encapsulating data within middle tier objects by opting to map GridView
s directly to DataSet
s and not the pertinent application objects. All of this compromise, just so sorting can work within the GridView
. In this brief article, I want to mention two techniques that I use to allow for mapping GridView
s to DataTable
s that are built upon existing middle tier objects. I tend to think of this as a pseudo adapter pattern that converts my model objects into DataTable
s. A benefit of this is that I can leverage all my previously written middle tier logic while also realizing the great features of the GridView
control like sorting. The two techniques are:
- Using reflection to initialize middle tier "model" layer objects.
- Using reflection to convert middle tier "model" layer objects into
DataTable
s.
Prior to getting into the code, I should mention the layers I typically have in my application. This will make it easier to conceptualize where I employ the techniques above.
- Model Layer – This layer holds objects that encapsulate data. This layer consists of all lightweight serializable classes that declare properties only. No methods whatsoever in this layer. These are the classes that I use to pass data through the various layers of the application.
- Controller Layer – This layer is the glue between my ASP.NET application and the Business Logic Layer.
- Business Logic Layer – The Business Logic Layer acts on the data within the model layer. This layer also interfaces with the data logic layer by "asking" the data layer to perform CRUD (Create, Retrieve, Update, and Delete) operations on Model Layer objects.
- Data Logic Layer – This layer is used to perform the actual CRUD operations on the Model Layer objects.
The first technique that I use is within the Data Logic Layer. Once I retrieve a DataSet
from a database, I create and initialize the pertinent Model Layer class using reflection. In essence, this method iterates through all the columns of a row in a DataSet
and invokes the pertinent set methods within my model object. The invocation occurs only when my model object has property names the match the column names within the DataSet
. The result of this method is an initialized Model Layer object.
Public Function InitializeFromDataRow(ByVal aDataRow _
As System.Data.DataRow, ByVal aType _
As System.Type) As Object
Implements IDLL.IBaseDataObject.InitializeFromDataRow
Try
Dim props As PropertyInfo()
Dim myObject As Object
myObject = Activator.CreateInstance(aType)
Dim p As PropertyInfo
props = aType.GetProperties()
For Each p In props
Dim params(0) As Object
If aDataRow.Table.Columns.Contains(p.Name) = True Then
params(0) = aDataRow.Item(p.Name)
p.GetSetMethod.Invoke(myObject, params)
End If
Next
Return myObject
Catch ex As Exception
Throw ex
End Try
Return Nothing
End Function
The second technique that I use is in the Controller Layer. Simply put a class within the Controller Layer that asks the Business Logic Layer to convert a Model Layer object into a DataTable
. This enables me to map my GridView
to my model objects in a round about way. I say a round about way because all I am really doing is taking a Model Layer object and converting it to a DataTable
. This is where the adapter like pattern comes into play.
Public Function ConvertToDataTable(ByVal list As _
System.Collections.IList, _
ByVal aType As System.Type) As DataTable
Try
Dim aDataTable As New DataTable
Dim props As PropertyInfo()
Dim p As PropertyInfo
props = aType.GetProperties()
Dim anObject As Object
For Each p In props
aDataTable.Columns.Add(New _
DataColumn(p.Name, p.PropertyType))
Next
For Each anObject In list
Dim row(props.Length - 1) As Object
Dim i As Integer
i = 0
row(props.Length - 1) = New Object()
For Each p In props
row(i) = p.GetValue(anObject, Nothing)
i = i + 1
Next
aDataTable.Rows.Add(row)
Next
Return aDataTable
Catch ex As Exception
Throw ex
End Try
Return Nothing
End Function
Summary
What makes this possible is reflection. I have attached a very simple web application that makes use of the two techniques discussed above. Note, in the attached source code, the first grid results in an exception upon sorting while the second grid works like a charm. The key line of code in the example that circumvents the exception is:
Return myObject.ConvertToDataTable(myEmployees, _
GetType(IModelLayer.IEmployee))
This returns a DataTable
representation of an Employee Model object that my GridView
can play nicely with.
Contact the author: Brian Rush.