DNN Community Blog

The Community Blog is a personal opinion of community members and by no means the official standpoint of DNN Corp or DNN Platform. This is a place to express personal thoughts about DNNPlatform, the community and its ecosystem. Do you have useful information that you would like to share with the DNN Community in a featured article or blog? If so, please contact .

The use of the Community Blog is covered by our Community Blog Guidelines - please read before commenting or posting.

A DotNetNuke Reality Check

Original Module: VCLinks_01.02.00_Install.zip

Revised Module: VCLinks_02.00.00_Install.zip

This is a reality check not for the developer who's code is being examined but for myself, the "DotNetNuke Module Development Evangelist". The code examined here achieves the most important requirement. It works. The challenge for the "Evangelist" is to provide suggestions that offer improvements in time and efficiency. Just saying that "this is the recommended way" is not good enough. Learning new techniques is a costly process itself. This must be factored into the total cost. No developer can be expected to do anything that is more costly than the alternative.

The Module

The module being examined is called VC Links. It allows an administrator to define categories and add links to each category. 

Module Structure

The graphic below shows the module structure:

Module Navigation

The entry point for the application is "VCLinks.ascx". It implements the Dynamically Loaded Control pattern. The query string for each link is examined and the appropriate user control is injected.

If (Request("LinkID") <> "") Then
If (IsNumeric(Request("LinkID"))) Then
strControl = "ucAddLink.ascx"
End If
If (Request("LinkDetail") <> "") Then
If (IsNumeric(Request("LinkDetail"))) Then
strControl = "ucLinkDetail.ascx"
strControl = "ucViewAll.ascx"
End If
strControl = "ucViewAll.ascx"
End If
End If


The recommended navigation is covered here: NavigateURL: How to make a link the reasons are covered here: Module Navigation Options.

Reality Check

While NavigateURL is the recommended approach it does not save any code. The primary objection to the Dynamically Loaded Control pattern that is can produce some odd behavior in certain situations because dynamically loaded controls do not save viewstate.  There are solutions. For example, Denis Bauer has created a Dynamic Placeholder that maintains the viewstate of dynamically loaded controls. A tutorial describing it's implementation can be found at this link.

Since the current code works and there is no code savings the recommendation is to leave the code as it is.

The Database Layer

The module performs the standard CRUD operations. To illustrate this the link administration will be examined and a new page that contains the same functionality will be created.

When a link is updated this method is run:

Private Sub UpdateLink()
Dim intCategory As Integer = ddlCategory.SelectedValue
Dim strLinkName As String = tbLinkName.Text
Dim strLinkURL As String = tbLinkURL.Text
Dim strLinkDescription As String = tbLinkDescription.Text
'Open Connection
Dim strConn As String = ConfigurationManager.AppSettings("SiteSqlServer")
Dim objConn As New SqlConnection(strConn)
Dim cmd As New SqlCommand
cmd.CommandText = "vcLinks_UpdateLink"
cmd.CommandType = CommandType.StoredProcedure
cmd.Connection = objConn
'Declare Parameter(s)
cmd.Parameters.AddWithValue("@intVCLinksID", VCLinksID)
cmd.Parameters.AddWithValue("@intCategory", intCategory)
cmd.Parameters.AddWithValue("@strLinkName", strLinkName)
cmd.Parameters.AddWithValue("@strLinkURL", strLinkURL)
cmd.Parameters.AddWithValue("@strLinkDescription", strLinkDescription)
cmd.Parameters.AddWithValue("@intUpdatedBy", Me.UserId)
cmd.Parameters.AddWithValue("@UpdatedDate", Date.Now)
'Get ID of Restaurant Just added 
End Sub

This code is very succinct and tight. It simply gathers values and passes them to the stored procedure. There is nothing wrong with this code. The data has to be gathered and passed to the database. This method logically seems as good as any other. This code calls this stored procedure:

ALTER PROCEDURE [dbo].[VCLinks_UpdateLink]
@intVCLinksID int,
@intCategory int,
@strLinkName varchar(100),
@strLinkURL varchar(100),
@strLinkDescription varchar(1000),
@intUpdatedBy int,
@UpdatedDate datetime
VCLinksCategoryID = @intCategory,
LinkName = @strLinkName,
LinkURL = @strLinkURL,
LinkDescription = @strLinkDescription,
UpdatedBy = @intUpdatedBy,
UpdatedDate = @UpdatedDate
WHERE     (VCLinksID = @intVCLinksID)

Again, the stored procedure is simple and straitforward. Looking at it, there is nothing "wrong" that stands out. You could not find fault because the DotNetNuke DAL is not being used because the DotNetNuke DAL is meant to be used in situations where you need to support multiple databases. For this module that is not the case. In addition the DotNetNuke DAL would require far more code.

While the ExecuteSQL method of the DAL+ can be used to eliminate the need to create the stored procedures, it will not save very much work or time because essentially the same code that is in the stored procedure still needs to be created in the ExecuteSQL method.


The recommendation is to use Linq to SQL to save the amount of code needed. In addition the wizards provided by Visual Studio, when you use declarative data binding to strongly typed data sources, will also save considerable time when creating the user interface. 

Currently the functionality to administer the links is contained in the following page:

  • ucAddLink.ascx.vb (387 lines of code)

The assumption we will make is that reducing this code will reduce development time and the amount of potential coding errors that have to be debugged. The stored procedures will be eliminated also but this is a by product of implementing the solution.

Create the Linq to SQL DAL

This tutorial provides an in-depth demonstration of using Linq to SQL: Creating a DotNetNuke Module using LINQ to SQL (VB and C#).

For this module, we will simply create a Linq to SQL class and drag the two tables the module uses onto it and then save the page. The DAL is created in less than a minute. This "Linq to SQL DAL" is a class that is automatically created that contains classes that are mapped directly to the database and provide the functionality to manipulate the database and it's data. In addition the Linq to SQL class provides change tracking.

The Linq Data Source control can be used to configure automatic Creates, Reads, Updates, and Deletes.

It allows for common record selection requirements to be configured using the wizard. For example this code:

Private Sub ReadQueryString()
If (Request("LinkID") <> "") Then
If (IsNumeric(Request("LinkID"))) Then
VCLinksID = CInt(Request.QueryString("LinkID"))
End If
VCLinksID = Null.NullInteger
End If
End Sub

Can be configured using the wizard:

When the FormView is placed on the page and connected to the Linq Data Source control, the form is automatically created.

We need only to remove unneeded fields, add JavaScript validation controls and fix the formatting.

The new page to handle the same functionality:

  • ucAdminLinks.ascx.vb (80 lines of code).

Most importantly it takes a lot less time to create. 

Handling Business Rules

The module also contains this code to determine if a link already exists:

Protected Sub cvalLinkNameExist_ServerValidate
(ByVal source As Object, ByVal args As
Handles cvalLinkNameExist.ServerValidate
'Check to see if LinkName Already Exists
Dim intExists As Integer
'Open Connection
Dim strConn As String = ConfigurationManager.AppSettings("SiteSqlServer")
Dim objConn As New SqlConnection(strConn)
Dim cmd As New SqlCommand
cmd.CommandText = "vcLinks_Validate"
cmd.CommandType = CommandType.StoredProcedure
cmd.Connection = objConn
'Declare Parameter(s)
cmd.Parameters.AddWithValue("@ColumnNameValue", tbLinkName.Text)
'Get ID of Restaurant Just added 
intExists = cmd.ExecuteScalar()
If VCLinksID > 0 Then
args.IsValid = "True"
If intExists > 0 Then
args.IsValid = "false"
args.IsValid = "true"
End If
End If
End Sub

Again, we can use Linq to SQL to not only save a few lines of code but to also create a business rule that will be enforced no matter what UI control calls it. We simply add this partial class to the Linq to SQL class:

Private Sub InsertVCLink(ByVal instance As VCLink)
Dim results = From link In VCLinks.AsQueryable _
Where link.LinkName = instance.LinkName _
Or link.LinkURL = instance.LinkURL _
Select link
If results.Count() > 0 Then
Throw New Exception("Link Name or Url is already being used")
End If
End Sub

And this code to catch a possible exception and indicate that the insert was not performed:

Protected Sub FormView1_ItemInserted(ByVal sender As Object, 
ByVal e As System.Web.UI.WebControls.FormViewInsertedEventArgs)
Handles FormView1.ItemInserted
If e.Exception Is Nothing Then
If boolAddAnother Then
Null.NullString, "LinkID=0"))
Response.Redirect(NavigateURL(), True)
End If
lblError.Text = e.Exception.Message
e.ExceptionHandled = True
e.KeepInInsertMode = True
End If
End Sub


Programming is Art

Programming is art much the same way constructing a building is art. We desire to follow standard conventions to construct safe buildings, yet there is plenty of room for interpretation. The artistic aspects of programming is one of the things that motivates many of us to program. Yet, as the discovery of perspective was revolutionary for art, technologies such as Linq are also revolutionary. However, it is up to the programmer, the artist, to determine when, where, and how to apply them.

Note: The Linq to SQL version of the module does not support the object qualifier feature and in order to run it on a DotNetNuke site, you need to install ASP.NET 3.5 on the server and modify the web.config of the DotNetNuke site:

Change: the <system.codedom> section to:

<compiler language="vb;vbs;visualbasic;vbscript" type="Microsoft.VisualBasic.VBCodeProvider, System, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089" extension=".vb" warningLevel="4">
<providerOption name="CompilerVersion" value="v3.5"/>
<providerOption name="OptionInfer" value="true"/>
<providerOption name="WarnAsError" value="false"/>
<compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CSharp.CSharpCodeProvider,System, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089" warningLevel="4">
<providerOption name="CompilerVersion" value="v3.5"/>
<providerOption name="WarnAsError" value="false"/>

Change: the <assemblies> section to:

<add assembly="Microsoft.VisualBasic, Version=, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
<add assembly="System.DirectoryServices, Version=, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
<add assembly="System.Design, Version=, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
<add assembly="System.Management, Version=, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
<add assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<add assembly="System.Core, Version=, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<add assembly="System.Xml.Linq, Version=, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<add assembly="System.Data.DataSetExtensions, Version=, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<add assembly="System.Data.Linq, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089" />


Comment Form

Only registered users may post comments.


Aderson Oliveira (17)
Alec Whittington (11)
Alex Shirley (10)
Andrew Hoefling (2)
Andrew Nurse (30)
Andy Tryba (1)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (35)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Bogdan Litescu (1)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (211)
Chris Paterra (55)
Clinton Patterson (67)
Cuong Dang (21)
Daniel Bartholomew (2)
Daniel Mettler (162)
Daniel Valadas (1)
Dave Buckner (2)
David Poindexter (8)
David Rodriguez (3)
Dennis Shiao (1)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (75)
Francisco Perez Andres (4)
Geoff Barlow (12)
George Alatrash (12)
Gifford Watkins (3)
Gilles Le Pigocher (3)
Ian Robinson (7)
Israel Martinez (17)
Jan Blomquist (2)
Jan Jonas (3)
Jaspreet Bhatia (1)
Jenni Merrifield (6)
Joe Brinkman (274)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Kelly Ford (3)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matthias Schlomann (15)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Miguel Gatmaytan (3)
Mike Horton (19)
Mitchel Sellers (29)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Peter Donker (54)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Sacha Trauwaen (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott S (11)
Scott Wilkinson (3)
Scott Willhite (97)
Sebastian Leupold (80)
Shaun Walker (237)
Shawn Mehaffie (17)
Stefan Cullmann (12)
Stefan Kamphuis (12)
Steve Fabian (31)
Steven Fisher (1)
Timo Breumelhof (24)
Tony Henrich (3)
Torsten Weggen (2)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (40)
Will Strohl (165)
William Severance (5)

Content Layout

Subscribe to DNN Digest

Subscribe to DNN Digest

DNN Digest is our monthly email newsletter. It highlights news and content from around the DNN ecosystem, such as new modules and themes, messages from leadership, blog posts and notable tweets. Keep your finger on the pulse of the ecosystem by subscribing.  

Copyright 2018 by DNN Corp Terms of Use Privacy
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out