Products

Solutions

Resources

Partners

Community

About

New Community Website

Ordinarily, you'd be at the right spot, but we've recently launched a brand new community website... For the community, by the community.

Yay... Take Me to the Community!

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.


Advanced Module Development: Settings Management

Hi there again. Today’s topic for some deep exploration is how to manage your module’s settings in code. As I stated in my previous posts (here and here), most examples of DotNetNuke module development out in the wild are fine for a first shot at a module, but won’t hack it when you begin to get serious with module development. And if you don’t believe me: come to a DNN event and ask any one of the more experienced DNN Store vendors. It’s not that the Hello World examples suck. It’s just not sustainable when you’re building your module for world domination.

So once again: hi, my name is Peter Donker. I run a small business called Bring2mind which specializes in document management on the DotNetNuke platform.

Me with my DNN wizard hat on.

Module settings at the “Hello World” level

Even the most trivial of modules will have some settings that the admin/editor can change to control behaviour. Commonly we store these in a “property bag” called Settings which is a property of the base class of our module’s code (PortalModuleBase). This property bag comes in the form of a hashtable. So a list of object/object pairs. The values of these stored in a table called ModuleSettings … as strings. So the objects will get serialized to strings. What does that mean? Well, if you add a setting as follows:

Me.Settings.Add(“Foo”, bar)

Where bar is not a string, then .net will attempt to convert that to a string for you. This, already, is not a good idea. You don’t want .net to control your serialization unless you’re absolutely sure it does it correct both ways. Another thing to dislike about it is that it expects the first parameter to be a string or to convert to one. It is implicit. Not explicit. Nowadays we’d use a NameValueCollection to do this, but those weren’t around when Module Settings were first invented. So tough luck. You’re going to have to keep an eye on those conversions yourself.

Now when we add or draw out information from our settings we use that first parameter as identifier. Duh. That’s simple enough. So

FacebookAppId = CStr(Settings(“FacebookAppId”))

Is what you’d typically see. Note you get the ugly conversion bits floating around here as the hastable returns objects (unless you are programming with option strict off, which I’d never do). Of course this doesn’t work when the property bag is empty or this value has not been added yet. You’ll get a big fat error as null doesn’t convert to a string. So typically we’d see this:

If Not Settings("xmlsrc") Is Nothing
 xmlsrc = CType(Settings("xmlsrc"), String)
End If

This is pretty bulky to just retrieve a setting. And note you are writing the name of the setting twice in your code. Which is a pity as it violates the rule “keep the amount of times you write a key as a plain string in your code to a bare minimum”. Reread that and if necessary post it above your monitor. You would not be the first one to spend hours debugging an issue that was caused by a misspelt key somewhere. I have worked on code where the above pattern was repeated across several ascxs in the same project and even in several methods within those ascxs. This explodes the number of times you’re writing your key value.

Step 1: Taking the settings aside in their own class

The first thing to do is to do away with this diarrhea of hastable conversions across your code. Get all of this abstracted into your own settings class. Your class will have regular properties with their types instead of dealing with the flux of objects and implicit conversions. This is simple enough. You get something like this:

Public Class ModuleSettings

  Private _moduleId As Integer = -1
  Private _DefaultCacheTime As Integer = 30

  Public Property DefaultCacheTime() As Integer
    Get
      Return _DefaultCacheTime
    End Get
    Set(ByVal Value As Integer)
      _DefaultCacheTime = Value
    End Set
  End Property

  Public Sub New(ByVal ModuleId As Integer)

    _moduleId = ModuleId
    Dim settings As HashTable = (New DotNetNuke.Entities.Modules.ModuleController).GetModuleSettings(ModuleId)

    If settings("DefaultCacheTime") IsNot Nothing Then
     DefaultCacheTime = CInt(settings("DefaultCacheTime"))
    End If

  End Sub

  Public Sub Save()
    Dim objModules As New DotNetNuke.Entities.Modules.ModuleController
    objModules.UpdateModuleSetting(_moduleId, "DefaultCacheTime", Me.DefaultCacheTime.ToString)
  End Sub

End Class

This is already a lot better. Here we see that now we can use the property DefaultCacheTime in our code. As a bonus: it has been primed with a value in case there are no settings! We are now half way to enlightenment. We have now reduced the occurrence of “DefaultCacheTime” as a string to 3. But we can do better as we’ll see later. But first let’s talk inheritance.

Step 2: Completely hiding the hashtable

The next step is to override the PortalModuleBase and inject a class of your own that your ascxs inherit from. This allows you to intervene and remove the ugly duckling Settings and replace it with the beautiful swan version of Settings:

Public Class Modulebase
 Inherits Entities.Modules.PortalModuleBase
 Private _modSettings As ModuleSettings

 Public Shadows Property Settings() As ModuleSettings
  Get
   If _modSettings Is Nothing Then
    _modSettings = ModuleSettings.GetModuleSettings(ModuleId)
   End If
   Return _modSettings
  End Get
  Set(ByVal value As ModuleSettings)
   _modSettings = value
  End Set
 End Property

End Class

As you can see the Settings shadow the base settings. So now in your codebehind you’d write:

Settings.DefaultCacheTime

Your enlightenment is now almost complete. We will now revisit the ModuleSettings class you created.

Step 3: Perfecting the settings class

One thing I’m not that happy with is the “if settings(‘foo’) is not nothing then read its value”. I find it bulky and when you have a lot of settings it begins to really create tons and tons of code. So here’s a new radical idea: let’s draw out those values with extension methods. Check out this method:

  
  Public Sub ReadValue(ByRef ValueTable As Hashtable, ByVal ValueName As String, ByRef Variable As Integer)
   If Not ValueTable.Item(ValueName) Is Nothing Then
    Try
     Variable = CType(ValueTable.Item(ValueName), Integer)
    Catch ex As Exception
    End Try
   End If
  End Sub

This does the conversion based on its availability and even puts the Try-Catch in case the value was not really an integer. The result is that Variable will only be loaded if that value has been found and is a valid integer. This is what the logic should do. You can even make this a function returning a boolean if the value was read or not. For now I’ll leave it as a method. So now our code in the settings constructor becomes:

settings.ReadValue(“DefaultCacheTime”, DefaultCacheTime)

And we overload with all other data types that we need (pay especial attention to date conversions!). There is another minor benefit to this approach: now I get to see the New and Save methods on one screen in code and I can easily spot any omissions or misspellings. The code becomes really compact and we have limited the string key values to 2! One for reading and one for writing. Note: you could of course create a variable for the key name and reduce that to 1. And I’m not against that. But 2, especially when you can see on the same page, is in my opinion an adequate reduction of failure risk already.

Final Words

I should also mention IPortable. Implementing this requires you to serialize and deserialize your settings. You can now offload that code to your settings class and make sure this stays in one place.

So there you have it. This is how I approach settings. It’s typically the first thing I tackle when I get involved in an existing module project (Blog/Newsfeeds). It is not hard to do and it really cleans up a lot of code (the blog module code is still marred by other issues, but that’d be the topic of another post). Hopefully the extension methods will make it into the DNN core one day. In my Document Exchange I have now created them for various types of variables and collections. So I can also use them on Request.Params for instance. Simple and effective.

Comments

There are currently no comments, be the first to post one.

Comment Form

Only registered users may post comments.

NewsArchives


Aderson Oliveira (22)
Alec Whittington (11)
Alessandra Daniels (3)
Alex Shirley (10)
Andrew Hoefling (3)
Andrew Nurse (30)
Andy Tryba (1)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (37)
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 (213)
Chris Paterra (55)
Clint Patterson (108)
Cuong Dang (21)
Daniel Bartholomew (2)
Daniel Mettler (181)
Daniel Valadas (48)
Dave Buckner (2)
David Poindexter (12)
David Rodriguez (3)
Dennis Shiao (1)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (80)
Francisco Perez Andres (17)
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 (4)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matt Rutledge (2)
Matthias Schlomann (16)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Miguel Gatmaytan (3)
Mike Horton (19)
Mitchel Sellers (40)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Oliver Hine (1)
Patricio F. Salinas (1)
Patrick Ryan (1)
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 Schlesier (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)
Tony Henrich (3)
Torsten Weggen (3)
Tycho de Waard (4)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (40)
Will Strohl (180)
William Severance (5)
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out