This article is based on material originally published on DNN Developer Zone (www.dnndevzone.com). This version has been updated to cover the changes made in DotNetNuke 5.0.
In a previous blog I discussed how the CBO class is used to hydrate custom business objects. The CBO class's methods are useful as they provide a simple API for developers building Modules. Using the Links module as an example, there are two methods in the LinksController class that use the CBO class (see Listing 1)
Listing 1: The GetLink and GetLinks methods of the LinksController
1: Public Function GetLink(ByVal ItemID As Integer, ByVal ModuleId As Integer) As LinkInfo
2: Return CType(CBO.FillObject(DataProvider.Instance().GetLink(ItemID, ModuleId),
3: End Function
5: Public Function GetLinks(ByVal ModuleId As Integer) As ArrayList
6: Return CBO.FillCollection(DataProvider.Instance().GetLinks(ModuleId),
7: End Function
The CBO class is very useful as it abstracts away the details of hydrating your objects. It also handles the management of the DataReader, making sure that the DataReader is closed when finished with. This is very important as the DataReader keeps a connection open to the Database.
There is however a cost in this abstraction - performance. The CBO HydrateObject private method uses Reflection to set the objects property values to the columns in the DataReader, and Reflection is typically slow. In DotNetNuke version 4.4.0 I introduced some custom hydration methods into the core so that mission-critical classes were hydrated explicitly, avoiding the performance hit from Reflection.
So if we use one of the CBO classes Fill... methods we have a performance hit from the sue of Reflection, and if we use a custom hydrator we have the responsibility of managing the DataReader pplus the complexity of creating the custom hydrators.
The IHydratable Interface
To solve this problem the IHydratable Interface was introduced in DotNetNuke version 4.6.0. This interface consists of a property and a method. (see Listing 2)
Listing 2: The IHydratable Interface
1: Public Interface IHydratable
2: Property KeyID() As Integer
3: Sub Fill(ByVal dr As IDataReader)
4: End Interface
The KeyID property identifies a unique identifier for the instance of the object. This is used by the FillDictionary method as the key for filling the collection, and for the most part can be implmented to wrap the existing identifier (ItemID in the case of the LinksInfo class)
Listing 3: The KeyID property implemented in LinksInfo
1: Public Property KeyID() As Integer Implements IHydratable.KeyID
3: Return ItemId
4: End Get
5: Set(ByVal value As Integer)
6: ItemId = value
7: End Set
8: End Property
The Fill method is the important member of the Interface for the purposes of this discussion (see Listing 4).
Listing 4: The Fill method implemented in LinksInfo
1: Public Sub Fill(ByVal dr As System.Data.IDataReader) Implements IHydratable.Fill
2: ItemId = Convert.ToInt32(Null.SetNull(dr("ItemId"), ItemId))
3: ModuleId = Convert.ToInt32(Null.SetNull(dr("ModuleId"), ModuleId))
4: Title = Convert.ToString(Null.SetNull(dr("Title"), Title))
5: Url = Convert.ToString(Null.SetNull(dr("URL"), Url))
6: ViewOrder = Convert.ToInt32(Null.SetNull(dr("ViewOrder"), ViewOrder))
7: Description = Convert.ToString(Null.SetNull(dr("Description"), Description))
8: CreatedByUser = Convert.ToInt32(Null.SetNull(dr("CreatedByUser"), CreatedByUser))
9: CreatedDate = Convert.ToDateTime(Null.SetNull(dr("CreatedDate"), CreatedDate))
10: TrackClicks = Convert.ToBoolean(Null.SetNull(dr("TrackClicks"), TrackClicks))
11: NewWindow = Convert.ToBoolean(Null.SetNull(dr("NewWindow"), NewWindow))
12: End Sub
This method sets the value of each property from the related column of the DataReader - without using Reflection, as we know which column relates to which property. The CBO class's FillObjectFromReader method checks if the object implements IHydratable, and if it does it casts the object as IHydratable and calls the Fill method. The Fill method (see Listing 4) processes the DataReader and returns control to the CBO which manages the DataReader and returns the object to the calling method.
The advantage of this approach is that the only difference between the approaches is that one requires the implementation of an Interface. When you are first developing a new Module you should use the CBO methods, without implementation of IHydratable and then implement the interface when you are sure that the performance gains warrant the extra work. Either way, you should be confident that the DataReader is managed and you are not leaving any open database connections.