Register | Login
Thursday, August 28, 2008

Sections
  
About Us
  
Hosting Provided by Server Intellect
Partners
Downloads
  
 WWWCoder.com Resource Directory

 

ORDER TODAY!
Professional DotNetNuke ASP.NET Portals
Amazon.com now has the only DotNetNuke book you'll ever need. Written by the guys who brought you DotNetNuke, Shaun Walker, Patrick Santry, Joe Brinkman, Dan Caron, Scott McCulloch, Scott Willhite, and Bruce Hopkins. Order it now! .

DotNetNuke (DNN) 2.x Module Architecture, Part II
5/15/2004 10:43:42 AM

In this second part of a three part series we will cover the Business Logic Layer, and the Data Abstraction Layer of module Architecture of DotNetNuke.

If you're not familiar with DotNetNuke, be sure you visit http://www.dotnetnuke.com to learn more about this fast growing open source portal that you can use and develop against absolutely free.

In the previous article we discussed how to get your module project set up, we also began development on the user interface pieces of our module. In this article we're going into what makes the DNN architecture stand out from most .NET applications available, specifically the data provider model. The provider model is a dominant theme in the upcoming Visual Studio 2005 release, and is what makes DNN flexible enough to have any database as the backend. Here we will cover the Business Logic Layer, and Data Abstraction Layer of the DNN architecture, in part III of this series we will finish it up with an overview of the Database Provider class.

BLL, and DAL

I'm sure the previous acronyms are ones you've seen tossed around if you're an avid user of the ASP.NET forums. What do these stand for?

  • Business Logic Layer (BLL) - Contains all the logic that is specific to your business. For example, if you debit one account and then credit another account, this would occur in the BLL.
  • Data Abstraction Layer - This is the layer within your application that connects to the data provider. It contains overridable methods that your provider will over ride for it's specific vendor database. Your application's BLL will interface with the DAL via a custom business objects helper class (CBO).

The custom business object helper class is basically a class that populates a collection of objects or objects that you created within your module. Usually these objects are directly related to stored procedures or queries from your database.

Reflection versus Abstraction

There are definitely plenty of changes here in DNN 2.x, in DNN 1.x we obtained data from our database using reflection. The major problem with the previous architecture of DNN was the lack of a clear definition between our different layers, i.e. data tier, business logic, and user tiers. Now with the provider model we have a clear separation of these tiers.

Another big improvement in DNN 2.x architecture is performance. Thanks to abstraction and caching techniques there has been a huge performance increase as exhibited in the following metrics:

DotNetNuke 2.0 ( DAL enhancement )
Total number of requests: 				    	93,254 
Total number of connections: 				    	93,253 
  
Average requests per second: 				      	310.85 
Average time to first byte (msecs): 				2.37 
Average time to last byte (msecs): 				2.46 
Average time to last byte per iteration (msecs): 			29.58 
  
Number of unique requests made in test: 				12 
Number of unique response codes: 				1 

DotNetNuke 1.0.10 ( SqlCommandGenerator )
Total number of requests:					42,350
Total number of connections:					42,350
		
Average requests per second:					141.17
Average time to first byte (msecs):				6.02
Average time to last byte (msecs):				6.15
Average time to last byte per iteration (msecs):			116.94
		
Number of unique requests made in test:				17
Number of unique response codes:				2

As you can see response time has been improved phenomenally!

Business Logic Layer

Now let's get into the different classes you will need to create for interacting with a database. In part I of this series we examined the user interface pieces of the survey module that comes bundled with the DNN distribution, now lets look at the BLL piece. In the survey example the BLL class is the SurveyDB.vb, in your modules you would follow the same naming convention, i.e.: ModuleNameDB. Within this file is at least two classes for your module, a ModuleInfo class and a ModuleController, for this example SurveyInfo, and SurveyController. Here's a description of these two classes:

  • ModuleInfo: Sets and gets public properties for our objects being generated by our controller class.
  • ModuleController: Used for obtaining objects,, basically uses the custom business objects helper class to populate data from our data provider into a collection of objects.

Let's look at the SurveyInfo class and see what's going on in there:

Imports System
Imports System.Data
Imports DotNetNuke

Namespace YourCompanyName.Survey
    Public Class SurveyInfo
        ' local property declarations
        Private _SurveyId As Integer
        ::
        ' initialization
        Public Sub New()
        End Sub
        ' public properties
        Public Property SurveyId() As Integer
            Get
                Return _SurveyId
            End Get
            Set(ByVal Value As Integer)
                _SurveyId = Value
            End Set
        End Property
        ::
 End Class
        

Basically a simple class that is exposing prosperities for our controller class. Now let's look at the controller class:

Public Class SurveyController
  Public Function GetSurveys(ByVal ModuleId As Integer) As ArrayList
    Return CBO.FillCollection(DataProvider.Instance().GetSurveys(ModuleId), _
      GetType(SurveyInfo))
  End Function

  Public Function GetSurvey(ByVal SurveyID As Integer, _
      ByVal ModuleId As Integer) As SurveyInfo
    Return CType(CBO.FillObject(DataProvider.Instance().GetSurvey(SurveyID, ModuleId), _
      GetType(SurveyInfo)), SurveyInfo)
  End Function

  Public Sub DeleteSurvey(ByVal SurveyID As Integer)
    DataProvider.Instance().DeleteSurvey(SurveyID)
  End Sub

  Public Function AddSurvey(ByVal objSurvey As SurveyInfo) As Integer
    Return CType(DataProvider.Instance().AddSurvey(objSurvey.ModuleId, objSurvey.Question, _
      objSurvey.ViewOrder, objSurvey.OptionType, objSurvey.CreatedByUser), Integer)
  End Function

  Public Sub UpdateSurvey(ByVal objSurvey As SurveyInfo)
    DataProvider.Instance().UpdateSurvey(objSurvey.SurveyId, objSurvey.Question, _
      objSurvey.ViewOrder, objSurvey.OptionType, objSurvey.CreatedByUser)
  End Sub
End Class

.So what's going on the controller class? If you look at the code above you will see several methods, GetSurveys, GetSurvey, DeleteSurvey, AddSurvey, and UpdateSurvey. In most cases these routines directly correspond to a stored procedure contained within your database. For example, there is a GetSurveys stored procedure in our database that accepts ModuleID as a parameter, and returns a record. Now in the controller class you are doing something similar here, in the GetSurveys method you accept the ModuleID as input (which will be passed to the provider class for database execution). Then the return will be of type SurveyInfo. Remember in SurveyInfo we defined properties for our class, these properties normally correspond to fields contained within our database. We now create an collection object for our module to work with instead of a record set or data reader. This combined with the data provider classes is what provides abstraction from our vendor's database.

Looking more into this class you see that the GetSurveys method first calls a CBO method, which is passed an instance of our data abstraction class. The CBO or custom business object class is what takes the data from our database via the data abstraction class, and then populates a collection of objects as the return to our BLL. So the main concept here is we don't directly work with data from database calls, our module deals with a collection of objects that contain properties that we defined in our SurveyInfo class.

Another thing to note is when you are doing update, add, and delete operations there is no need to wrap your call within the CBO method, because in most cases there is no return coming from the database.

Database Abstraction

Now that we covered the BLL, let's cover abstraction a bit more, we know that methods in our BLL correspond with stored procedures (in the case of SQL), but how are these calls actually abstracted out? This abstraction is provided by our DataProvider.vb class in the Survey example. You can see from the code below, this class contains overridable methods that our actual SQLDataProvider class will provide. By abstracting out these methods in our DataProvider.vb abstraction class, all we need to do is add a new assembly for the database we want to use as a backend for DNN, no need to recompile or change our module in any way. This abstraction class becomes part of your actual module project.

 

Imports System
Imports System.Web.Caching
Imports System.Reflection

Namespace YourCompanyName.Survey
  Public MustInherit Class DataProvider
    ' provider constants - eliminates need for Reflection later
    Private Const [ProviderType] As String = "data" ' maps to  in web.config 
    Private Const [NameSpace] As String = "YourCompanyName.Survey" ' project namespace
    Private Const [AssemblyName] As String = "YourCompanyName.Survey" ' project assemblyname
    Public Shared Shadows Function Instance() As DataProvider
      Dim strCacheKey As String = [NameSpace] & "." & [ProviderType] & "provider"
      ' Use the cache because the reflection used later is expensive
      Dim objConstructor As ConstructorInfo = _
        CType(DotNetNuke.DataCache.GetCache(strCacheKey), ConstructorInfo)
      If objConstructor Is Nothing Then
        ' Get the provider configuration based on the type
        Dim objProviderConfiguration As DotNetNuke.ProviderConfiguration = _
          DotNetNuke.ProviderConfiguration.GetProviderConfiguration([ProviderType])
        ' The assembly should be in \bin or GAC, 
        ' so we simply need to get an instance of the type
        Try
          ' Override the typename if a ProviderName is specified 
          ' ( this allows the application to load a different 
          ' DataProvider assembly for custom modules )
          Dim strTypeName As String = [NameSpace] & "." & _
            objProviderConfiguration.DefaultProvider & _
            ", " & [AssemblyName] & "." & objProviderConfiguration.DefaultProvider
          ' Use reflection to store the constructor of the class that implements DataProvider
          Dim t As Type = Type.GetType(strTypeName, True)
            objConstructor = t.GetConstructor(System.Type.EmptyTypes)
          ' Insert the type into the cache
          DotNetNuke.DataCache.SetCache(strCacheKey, objConstructor)
        Catch e As Exception
          ' Could not load the provider - this is likely due to binary compatibility issues 
        End Try
      End If
    Return CType(objConstructor.Invoke(Nothing), DataProvider)
  End Function

' all core methods defined below

  Public MustOverride Function GetSurveys(ByVal ModuleId As Integer) As _
    IDataReader
  Public MustOverride Function GetSurvey(ByVal SurveyID As Integer, _
    ByVal ModuleId As Integer) As IDataReader
  Public MustOverride Function AddSurvey(ByVal ModuleId As Integer, ByVal Question As _
    String, ByVal ViewOrder As Integer, ByVal OptionType As String, _
    ByVal UserName As String) As Integer
  Public MustOverride Sub UpdateSurvey(ByVal SurveyId As Integer, ByVal Question As _
    String, ByVal ViewOrder As Integer, ByVal OptionType As String, ByVal UserName As String)
  Public MustOverride Sub DeleteSurvey(ByVal SurveyID As Integer)
  Public MustOverride Function GetSurveyOptions(ByVal SurveyId As Integer) As _ 
    IDataReader
  Public MustOverride Function AddSurveyOption(ByVal SurveyId As Integer, _
    ByVal OptionName As String, ByVal ViewOrder As Integer) As Integer
  Public MustOverride Sub UpdateSurveyOption(ByVal SurveyOptionId As Integer, _
    ByVal OptionName As String, ByVal ViewOrder As Integer)
  Public MustOverride Sub DeleteSurveyOption(ByVal SurveyOptionID As Integer)
  Public MustOverride Sub AddSurveyResult(ByVal SurveyOptionId As Integer)
 End Class
End Namespace

So let's go over what the code actually does in the abstraction class.

  • Asbstracts calls to the database using overridable methods.
  • Contains an instance() method which determines which data provider to use for our module (more about this later).
  • Contains all data access methods for your modules which are then forwarded to the data provider for direct interaction with a specific vendor database.

Look at the instance method of this class, you can see it checks to see which database provider we're going to use for our module, it will then load the correct provider and our provider will provide the direct database interaction for our module. The way the provider is specified in DotNetNuke is by modifying the Web.config file under the DotNetNuke key. For a default install of DNN, there are two providers available, Access, and SQL Server, by default Access is specified, but that can be changed by pointing the defaultProvider to SQLDataProvider. Let's look at the Web.config with this SQL modification.


<dotnetnuke>
   <data defaultProvider="SqlDataProvider" >
      <providers>
         <clear/>
         <add name = "SqlDataProvider" 
                        type = "DotNetNuke.Data.SqlDataProvider, DotNetNuke.SqlDataProvider" 
                        connectionString = "Server=localhost;Database=Demo;uid=sa;pwd=;" 
	                    providerPath = "~\Providers\DataProviders\SqlDataProvider\" 
                        objectQualifier = "" 
                        databaseOwner = "dbo" 
         />
        <add name = "AccessDataProvider" 
                        type = "DotNetNuke.Data.AccessDataProvider, DotNetNuke.AccessDataProvider" 
                        connectionString = "PROVIDER=Microsoft.Jet.OLEDB.4.0;"
	                    providerPath = "~\Providers\DataProviders\AccessDataProvider\" 
                        objectQualifier = "DotNetNuke" 
                        databaseFilename = "DotNetNuke.mdb.resources"
         />
     </providers>
   </data>
</dotnetnuke>

 

In part three of this series we will wrap up development by covering the SQLDataProvider. In the final part of this series we will explain how you can create a private assembly installation package for distribution.

By: Patrick Santry, Microsoft MVP (ASP/ASP.NET), developer of this site, author of books on Web technologies, and member of the DotNetNuke core development team. If you're interested in the services provided by Patrick, visit his company Website at Santry.com.

Related Articles
   Related Document DotNetNuke (DNN) 2.x Module Architecture, Part III
   Related Document DotNetNuke (DNN) 2.x Module Architecture, Part I


Page Options:
format for printing  Format for Printer
email article  Email Page
add to your favorites   Add to Favorites
How would you rate the quality of this content?
Poor - - Excellent
Comments?
Overall Rating:
Comments Left:
Left on 11/11/2007 7:52:48 PM by Anonymous
Comments: Hello! Good Site! Thanks you! yddumsbhmfwfzs
Left on 5/19/2005 11:00:04 PM by Anonymous
Comments: DNN 2 performance info, just what I was looking for!
Left on 1/5/2005 6:35:50 PM by Anonymous
Comments: Extremely poorly formated on IE
Left on 12/24/2004 10:17:53 AM by Anonymous
Comments: This is that I was looking for...
Left on 12/11/2004 4:17:24 AM by Anonymous
Comments: Comments from the following blog: icfarmer, located at: http://www.cnblogs.com/icfarmer/archive/2004/12/11/75639.html
No ratings available.
Left on 11/25/2004 6:52:26 AM by Anonymous
Comments: At last, a decent complete guide to building a DNN module!
  

 Latest Articles
  

 Latest News
  

 

Spotlight
Syndication

 


 


Digg This
 


DotNetNuke Platinum Benefactor

  
 

 Terms Of Use | Privacy Statement
 Copyright 2008 - Santry Technology Solutions, Box 172, Girard, PA 16417, (814) 774-0970