DotNetNuke Architecture - Digging Into the DNN Source Code

I installed DotNetNuke (DNN) this weekend for the Sarasota, Florida .NET Developer Group website.  Since I had DNN on my test PC, I decided to view the source code to see how it was architected.  I was pleasantly pleased at how well organized the code was as well as how closely it resembled the Community Server source code.  DNN is written in VB.NET and Community Server is written in C#, but the architectures are very similar.

Since they are so similar, let's walk through the source code like we did with Community Server in the following post:

Community Server Source Code - Abstract Classes, Reflection and Data Providers

One of the modules in DotNetNuke is the HTML Module.  The module is responsible for displaying blocks of HTML for the portal.  The usercontrol is called HtmlModule.ascx and if you look at the VB.NET in the code-behind you will see the following code snippet shown below. In Page_Load, the module instantiates a controller, of type HtmlTextController, to go out and get the HTML contents for this module based on its ModuleId. (For more information on controller, see Applying UML and Patterns: Controller GRASP Pattern - Model View Controller Design Pattern - First Object Beyond UI Layer.

 

HtmlModule.ascx.vb
Private Sub Page_Load

    ' ...
    
    Dim objHTML As New HtmlTextController
    Dim objDr As HtmlTextInfo = 
objHTML.GetHtmlText(ModuleId)
' ... End Sub

 

The GetHtmlText function shown below has a few interesting things, many of which we talked about with the Community Server source code.  DataProvider.Instance() is a Factory Method that returns the concrete Data Provider class for the HTML Module.  GetHtmlText(...) is then called on this concrete class, which returns an object of IDataReaderCBO.FillObject is a helper function that will take an IDataReader and return a simple "hydrated" business object - in this case an object of type HtmlTextInfo.

 

HtmlTextController.vb
Public Function GetHtmlText
(
ByVal moduleId As Integer) As HtmlTextInfo Return CType(CBO.FillObject(DataProvider.Instance()
.GetHtmlText(moduleId),
GetType(HtmlTextInfo)), HtmlTextInfo) End Function

 

Let's get back to the DataProver.Instance() factory method.  DataProvider is an abstract class from which the concrete data provider will inherit.  The Instance() method returns the concrete data provider:

 

DataProvider.vb
Public MustInherit Class DataProvider

#Region "Shared/Static Methods"

   ' singleton reference to the instantiated object 
   Private Shared objProvider As DataProvider = Nothing

   ' constructor
   Shared Sub New()
       CreateProvider()
   End Sub

   ' dynamically create provider
   Private Shared Sub CreateProvider()
        objProvider = CType(Framework.
Reflection.
CreateObject("data", "DotNetNuke.Modules.Html",
"DotNetNuke.Modules.Html"), DataProvider) End Sub ' return the provider Public Shared Shadows Function Instance() As DataProvider Return objProvider End Function #End Region #Region "Abstract methods" ' ... Public MustOverride Function GetHtmlText(ByVal moduleId
As Integer) As IDataReader ' ... #End Region End Class

 

The concrete data provider is dynamically created on the fly using reflection.  The CreateProvider subroutine above calls Framework.Reflection.CreateObject (...) to create the object based on the parameter values sent to the helper class as well as the default "data" provider in the web.config file:

 

Reflection.vb
Public Class Reflection

    Public Shared Function CreateObject(ByVal
ObjectProviderType As String) As Object Return CreateObject(ObjectProviderType, "", "") End Function Public Shared Function CreateObject(ByVal
ObjectProviderType As String, ByVal ObjectNamespace As String,
ByVal ObjectAssemblyName As String) As Object Dim TypeName As String = "" Dim CacheKey As String = "" Dim objProviderConfiguration As
ProviderConfiguration = ProviderConfiguration.
GetProviderConfiguration(ObjectProviderType)
If ObjectNamespace <> "" And
ObjectAssemblyName <> "" Then TypeName = ObjectNamespace & "." & objProviderConfiguration.DefaultProvider & ", " & ObjectAssemblyName & "." & objProviderConfiguration.DefaultProvider CacheKey = ObjectNamespace & "." &
ObjectProviderType & "provider" Else TypeName = CType(objProviderConfiguration.Providers (objProviderConfiguration.DefaultProvider),
Provider).Type CacheKey
= ObjectProviderType & "provider" End If Return CreateObject(TypeName, CacheKey) End Function Public Shared Function
CreateObject(ByVal TypeName As String, ByVal CacheKey As String) As Object Dim objObject As Object If CacheKey = "" Then CacheKey = TypeName End If Dim objType As Type =
CType(DataCache.GetCache(CacheKey), Type) If objType Is Nothing Then Try objType = Type.GetType(TypeName, True) DataCache.SetCache(CacheKey, objType) Catch exc As Exception LogException(exc) End Try End If Return Activator.CreateInstance(objType) End Function End Class

 

Below is a snippet of the actual concrete class for Sql Server that is getting the html for the HTML Module. As I mentioned above, it only returns an IDataReader.  It is the CBO.FillObject(...) method that hydrates the IDataReader information into an HtmlTextInfo object.

 

SqlDataProvider.vb
Public Class SqlDataProvider
    Inherits DataProvider
    ' ...
    
    Public Overrides Function
GetHtmlText(ByVal moduleId As Integer) As IDataReader Return CType(SqlHelper.ExecuteReader(ConnectionString, DatabaseOwner & ObjectQualifier & "GetHtmlText", moduleId), IDataReader) End Function ' ... End Class

 

As you can tell from the code and description above, DotNetNuke and Community Server share a lot of the same ideas.  You essentially have a usercontrol who's code-behind calls a controller class.  The controller class calls an Instance() factory method on an abstract data provider class that returns the concrete data provider class.  Often this concrete class is instantiated using Reflection.  The data call is handed off to the concrete data provider class that returns, in this case, an IDataReader.  A business object is hydrated from the IDataReader class and the data reader and connection are closed.  And, last, the contents are dispayed in the usercontrol.

In the near future, I will build a similar architecture using the Create a Shopping Cart object so that we can add persistence to the class.

posted on Sunday, April 03, 2005 10:31 PM

Main

News

Green Tea

.NET Development

Enterprise Library

Patterns & Practices