October 2007 Entries

image

When opportunity knocks, pull a Cash Peters.

I’ll be in Vegas for the OpenForce conference next week. Let me just say that getting there and back is in and of itself a colossal mission that only an insane person would embark upon. Traveling any great distance from Southern Illinois is similar to what you might experience if you were originating from a third-world country and trying to reach the moon.

I am not exactly certain what one does at these events, but I do have some general goals for the trip: imbibe information, meet a few people, and have fun.

What are your plans for the event?

Welcome back! This is part III of the Making Your DotNetNuke Module Do More For You series. Last time I brought you some ways to have your module interact with the DotNetNuke page that it lives on: Utilizing Your DotNetNuke Module's Data Access Layer.

Amongst other things, I showed you how to read and write settings for your module instance. Today I will show you how to read and write unique values for each DotNetNuke user of your module.

The Personalization Namespace

To store and retrieve values that are unique to one particular user you can use classes from the DotNetNuke.Services.Personalization namespace. We will first be interested in the Personalization class within this namepace. The DotNetNuke.Services.Personalization.Personalization class contains a number of static (aka shared) methods that will get, set, and remove custom key-value pairs of data for an authenticated user.

Storing Personalization Data

SetProfile(ByVal NamingContainer As String, ByVal Key As String, ByVal Value As Object)

This version of SetProfile will store an object in the profile of the current authenticated user. If there is no authenticated user, this method does nothing. The first parameter NamingContainer is exactly what it sounds like, a naming container. In this context the NamingContainer string represents some entity with which you wish to associate the data. As a rule of thumb a module developer will use the current module instance for the naming container, unless it makes sense to make the naming container a different scope, like the PortalId or a constant name used between related modules.

The second parameter is a string type that is used to create a key for retrieving the data you are storing. You should use a meaningful name here. For instance, if you are storing the date of the user’s last visit, you could use the string “last_visit” as a key. You might also consider using your module or company name here to help you identify that data if it is ever seen by human eyes at the DAL layer.

The final parameter of the SetProfile() method is the data you wish to store. The parameter is of type object, so you could in theory specify anything here. However, this data gets serialized to xml behind the scenes, so you might keep in mind that the object you specify needs to play well with an XmlSerializer. It might help to implement the IXmlSerializable interface if you want control over how your custom class gets serialized and de-serialized.

In this example we store the string value “Purple” under the key “Favorite Color”, using the current module as the naming container:

    1 DotNetNuke.Services.Personalization.Personalization.SetProfile(

    2    ModuleId.ToString(), "Favorite Color", "Purple");

The second method that the Personalization class has for storing data is an overloaded version of SetProfile() that takes an extra parameter in the front.

    1 SetProfile(ByVal objPersonalization As PersonalizationInfo, ByVal NamingContainer As String, ByVal Key As String, ByVal Value As Object)

This method does the same thing that the original SetProfile() method does, except it allows you to specify a PersonalizationInfo object that contains the profile of the user that you want to store personalization data for.

This can be very useful if you need to initialize personalization info for all the users in a portal:

    1 // save some blog space, in all examples

    2 using DotNetNuke.Services.Personalization;

    3 using DotNetNuke.Entities.Users;

...

    1 // Instantiate a PersonalizationController

    2 // to use for loading profiles

    3 PersonalizationController persController

    4    = new PersonalizationController();

    5 

    6 // Get array of UserInfo,

    7 // containing all users in portal

    8 ArrayList array = UserController.GetUsers(PortalId);

    9 

   10 // Process each UserInfo

   11 // to set profile data

   12 foreach (object userInfo in array)

   13 {

   14    // get PersonalizationInfo object for user

   15    PersonalizationInfo persInfo

   16         = persController.LoadProfile(

   17             ((UserInfo)userInfo).UserID, PortalId);

   18    if (persInfo != null)

   19    {

   20         // initialize personalization information

   21         Personalization.SetProfile(

   22             persInfo,

   23             ModuleId.ToString(),

   24             "Favorite Color",

   25             "Purple");

   26    }

   27 }

The code above retrieves an array of UserInfo type, one UserInfo object for each user in the portal. A PersonalizationInfo object is retrieved using data from each UserInfo object. Then the PersonalizationInfo object is used to call the overloaded SetProfile method.

Retrieving Personalization Data

GetProfile(ByVal NamingContainer As String, ByVal Key As String)

The GetProfile() method retrieves the data you stored using the SetProfile() method. The first parameter is the naming container you used when you called SetProfile() to store the data. The second parameter is the key you used to store the data using the SetProfile() method.

The GetProfile() method returns the requested data as a simple object type, so you must re-box the object to its original type. GetProfile() will return an empty string if no user is authenticated.

    1 string favColor =

    2    (string)Personalization.GetProfile(

    3         ModuleId.ToString(),

    4         "Favorite Color");

The code above retrieves the string “Purple” (assuming you first initialized the “Favorite Color” entry using the first SetProfile() example above).

Just like the SetProfile() method, the GetProfile() method also has an overloaded version which allows you to specify a PersonalizationInfo object, so you can read a value from the profile of a particular user that isn’t necessarily authenticated.

GetProfile(ByVal objPersonalization As PersonalizationInfo, ByVal NamingContainer As String, ByVal Key As String)

The overloaded GetProfile() method might be useful if your module needs to perform different actions for different users, based on a particular piece of data in the profile of the user. For instance the following code could be used to contact all the users, each using the method they prefer:

    1 // Instantiate a PersonalizationController

    2 // to use for loading profiles

    3 PersonalizationController persController

    4    = new PersonalizationController();

    5 

    6 // Get array of UserInfo,

    7 // containing all users in portal

    8 ArrayList array = UserController.GetUsers(PortalId);

    9 

   10 // Process each UserInfo

   11 // to set profile data

   12 foreach (object userInfo in array)

   13 {

   14    // get PersonalizationInfo object for user

   15    PersonalizationInfo persInfo

   16         = persController.LoadProfile(

   17             ((UserInfo)userInfo).UserID, PortalId);

   18    if (persInfo != null)

   19    {

   20         switch (

   21             (string)Personalization.GetProfile(

   22                persInfo,

   23                ModuleId.ToString(),

   24                "Contact Method"))

   25         {

   26             case "email":

   27                // call email method

   28                break;

   29             case "sms":

   30                // call sms service

   31                break;

   32         }

   33    }

   34 }

The code above loops through the profiles of every user in the portal and contacts them based on the personalization data stored with the key "Contact Method".

Removing Personalization Data

RemoveProfile(ByVal NamingContainer As String, ByVal Key As String)

The RemoveProfile() method does the reverse of the SetProfile() method. That is, it will remove the personalization entry for the authenticated user. If there is no authenticated user, this method does nothing.

This method takes the same parameters as the GetProfile() method. The first parameter is the naming container you used to store data. The second parameter is the key you used to store the data.

    1 Personalization.RemoveProfile(

    2    ModuleId.ToString(),

    3    "Favorite Color");

Like both SetProfile() and GetProfile(), this method has an overload that allows you to specify a PersonalizationInfo object containing the user profile you wish to remove a value from.

RemoveProfile(ByVal objPersonalization As PersonalizationInfo, ByVal NamingContainer As String, ByVal Key As String)

This could be used to remove a personalization setting for all the users in a portal:

    1 // Instantiate a PersonalizationController

    2 // to use for loading profiles

    3 PersonalizationController persController

    4    = new PersonalizationController();

    5 

    6 // Get array of UserInfo,

    7 // containing all users in portal

    8 ArrayList array = UserController.GetUsers(PortalId);

    9 

   10 // Process each UserInfo

   11 // to set profile data

   12 foreach (object userInfo in array)

   13 {

   14    // get PersonalizationInfo object for user

   15    PersonalizationInfo persInfo

   16         = persController.LoadProfile(

   17             ((UserInfo)userInfo).UserID, PortalId);

   18    if (persInfo != null)

   19    {

   20         // remove personalization information

   21         Personalization.RemoveProfile(

   22             ModuleId.ToString(),

   23             "Favorite Color");

   24    }

   25 }

The code above loops through the profiles of every user in the portal and removes the personalization data stored in the current module's naming container with the key "Favorite Color".

To Summarize

  1. You can store custom data for each user without creating custom DAL code by leveraging the DotNetNuke framework's Personalization namespace and Personalization class.
  2. The Personalization class has methods SetProfile(), GetProfile(), and RemoveProfile(), which can (respectively) store, retrieve, and delete custom user data.
  3. SetProfile(), GetProfile(), and RemoveProfile() each have an overload that allows you to specify a PersonalizationInfo object that contains the profile of a particular user that you wish to read or modify.

I hope you find the information in this article useful. Stay tuned for upcoming segments where I will show you how to dynamically create DotNetNuke pages (tabs) from within a module. I also hope to bring you a more detailed overview of the DotNetNuke user infrastructure.

Thanks for reading!

This is part II of the Making Your DotNetNuke Module Do More For You series. In Part I of this series I showed you how to start modifying your data access layer to achieve such things as passing stored procedure output parameter values up into your business logic layer and presentation layer.

Today I want to show you how to tackle some common tasks so that you can begin doing so right away. We will be focusing on how your module can change properties on the page as well as how you can set your module to change based on what page it lives on. Here is what we will be covering:

· How to modify data inside the <head> section of the DotNetNuke page.

· How to work with the DotNetNuke page URL.

· How to store and retrieve settings for an instance of your module.

So let’s get it on.

Changing the DotNetNuke Page Title and <head> Section Meta Data

In a typical .Net web application you can change the title of a page on the fly by accessing the Page object’s Title property.

  1 protected void Page_Load(System.Object sender,System.EventArgs e)

  2 {

  3    Page.Title = "My Custom Page Title";

  4 }

But from within your DotNetNuke module code you do it in a slightly different way. You first type-cast the Page object into a DotNetNuke.Framework.CDefault variable. Then you read and write the Title member on the CDefault object:

  1 protected void Page_Load(System.Object sender,System.EventArgs e)

  2 {

  3    DotNetNuke.Framework.CDefault cd

  4         = (DotNetNuke.Framework.CDefault)Page;

  5    cd.Title = "My Custom Page Title";

  6 }

If you compare Page.Title to CDefault.Title you will notice that Page.Title is a public property and CDefault.Title is a public member. What’s going on here?

image

image

Let’s take a look under the hood of the framework. Inside the CDefault source you will find this code:

   40 Public Class CDefault

   41    Inherits DotNetNuke.Framework.PageBase

   42 

   43    Public Comment As String = ""

   44    Public Description As String = ""

   45    Public KeyWords As String = ""

   46    Public Copyright As String = ""

   47    Public Generator As String = ""

   48    Public Author As String = ""

   49    Public Shadows Title As String = ""

You will notice that CDefault inherits from DotNetNuke.Framework.PageBase. Let’s take one step further and look at what the PageBase code looks like:

   50 Public MustInherit Class PageBase

   51 

   52    Inherits System.Web.UI.Page

You can see that PageBase inherits from the System.Web.UI.Page class. What is going on is that CDefault is a descendant of PageBase, and inherently a descendant of the Page class as well. Thus it has all the members and method of those two classes.

However, CDefault defines its own public string called Title which “shadows” the Page.Title property. This essentially hides the Page.Title from Visual Studio’s IntelliSense when you’re viewing the properties and methods of a CDefault object.

   49 Public Shadows Title As String = ""

So when you set CDefault.Title you are actually setting a different variable than Page.Title.

The nice thing about having the CDefault object around is that it exposes several other parts of the page for you to work with besides the title.

   44 Public Description As String = ""

   45 Public KeyWords As String = ""

   46 Public Copyright As String = ""

   47 Public Generator As String = ""

   48 Public Author As String = ""

These members allow you to set the values of meta-data in your page header.

    1 cd.Description = "My Custom Description";

    2 cd.KeyWords = "Coffee, Mojo, Hat";

    3 cd.Copyright = "My Company";

    4 cd.Generator = "Foo Coder";

    5 cd.Author = "Custom Author Name";

Will render as:

image

Note that in the code above, cd is a CDefault object.

Knowing how to set these values can be important for those of you concerned with search engine optimization.

Working With The DotNetNuke Page URL

As you probably already understand, the typical DotNetNuke installation really only has one page, Default.aspx. The Default.aspx page receives the tab id (the id of the page it needs to display) via the query string using URL Rewrite. Inside the SiteURLs.config file in the root folder of your installation there exists the following rule which tells the URL Rewritting Http Module to convert the search engine friendly URL into a URL that passes the tab id to the default.aspx page via the query string:

   28 <RewriterRule>

   29    <LookFor>[^?]*/TabId/(\d+)(.*)</LookFor>

   30    <SendTo>~/Default.aspx?TabId=$1</SendTo>

   31 </RewriterRule>

This is a powerful tool that you can customize in order to make your module more functional.

Just as the DotNetNuke site uses the URL rewriter to reduce the page files down to just one, you can use the URL rewriter to reduce the module instance quantity down to just one. Let me explain.

Let’s say that you have a database table containing 10,000 records, each of which you want to provide individual pages for. Rather than generating 10,000 tabs / pages inside your DotNetNuke installation, you can create a URL rewrite rule like this:

   12 <RewriterRule>

   13   <LookFor>.*/Products/(.+)/(.+)\.aspx</LookFor>

   14   <SendTo>~/Default.aspx?TabId=54&amp;brand=$1&amp;item=$2&amp;mode=view</SendTo>

   15 </RewriterRule>

In this rule you tell the URL rewriter to look for a regular expression that matches a request that looks like http://www.yourdomain.com/Products/SomeBrand/ProductName.aspx, where SomeBrand can be anything and ProductName can be anything. Those two “anythings” will be the key to one of the records you wish to display from your database table. The rule rewrites the URL to point to a specific tab (tab 54) on which there exists an instance of your module. This allows you to have a unique URL for each of the 10,000 products in your database, but only one tab within DotNetNuke for displaying them.

Now your module can display whatever it pleases based on the friendly URL that was originally requested! Have your module fetch the brand and item parameters through the query string:

    1 if (Request.QueryString["brand"] != null

    2    && Request.QueryString["item"] != null)

    3 {

    4    string path = Request.QueryString["brand"]

    5         + "/" + Request.QueryString["item"];

    6 

    7    // now use the path string as a

    8    // key to a record in your database

    9 }

Of course you need to take care not to pass on any old query string data on to your DAL. I usually run the values I get from the query string through some code like this which removes spaces and special characters:

    1 // remove non [A-Za-z0-9] with spaces

    2 myString = Regex.Replace(myString, "[^A-Za-z0-9]", " ");

    3 // remove spaces

    4 myString = myString.Replace("  ", " ").Trim();

For the sake of simplicity you might consider passing the numeric id / key of the item in your database rather than the brand and product name text like the example above. By doing so you can keep your data redundancy low inside your database, and eliminate the chance of two items having the same path in the URL. On the other hand the URL containing only text might be considered more optimized for search engines. This is a trade-off you will have to consider on your own.

Final, here is one note regarding URLs for the Asp.Net beginners reading this. You will probably at some point want to advance the browser to one of the product URLs manually. This can be done using the Response.Redirect() method.

    1 Response.Redirect("/Products/"

    2    + path + ".aspx");

 

Storing And Retrieving Settings For Your DotNetNuke Module

I am aware that this topic has already been covered by many fine tutorials out there. However, I want to mention it because there appears to be a major typo in the official Module Development Guide that is a show-stopper. So here is a brief explanation that will allow your show to go on!

You should first understand that you will usually want to store module settings per each instance of your module. This allows for individually configured instances of your module to live within the same portal.

To do this you must key off of your module instance’s id. You can access this id by referencing your module’s ModuleId property that is inherited from the PortalModuleBase class. You will be passing this value into methods that store the settings for your module’s instance.

To access a stored setting for your module from within your main view control code is easy. Just reference the Settings property that is inherited from PortalModuleBase. The Settings property is an associative array that keys on the name of the setting you used to store it in the first place.

    1 protected void LoadMySettings()

    2 {

    3    if (Settings["Mode"] != null)

    4    {

    5         _mode = Settings["Mode"].ToString();

    6    }

    7    if (Settings["ShowPicture"] != null)

    8    {

    9         _showpicture = bool.Parse(Settings["ShowPicture"].ToString());

   10    }

   11    if (Settings["ListTitle"] != null)

   12    {

   13         _title = Settings["ListTitle"].ToString();

   14    }

   15 }

Now on to storing you modules settings.

Storing your settings is more complex because it must be done from within a control that subclasses from the ModuleSettingsBase class. If you’ve read through the Module Development guide then you should know what I am talking about.

Your subclass is required to override the LoadSettings() method and the UpdateSettings() method. Inside your LoadSettings() method you read the settings using the ModuleSettings property that is inherited from the ModuleSettingsBase class. The Module Settings property is an associative array just like the Settings property of the PortalModuleBase class.

    1 public override void LoadSettings()

    2 {

    3    if (ModuleSettings["Mode"] == null)

    4    {

    5         ModuleSettings["Mode"] = "Small";

    6    }

    7    ModeDropDownList.SelectedValue =

    8         ModuleSettings["Mode"].ToString();

    9 

   10    if (ModuleSettings["ShowPicture"] == null)

   11    {

   12         ModuleSettings["ShowPicture"] = false;

   13    }

   14    ShowPictureCheckBox.Checked =

   15         bool.Parse(ModuleSettings["ShowPicture"].ToString());

   16 

   17    if (ModuleSettings["ListTitle"] == null)

   18    {

   19         ModuleSettings["ListTitle"] = "Title";

   20    }

   21    TitleTextBox.Text =

   22         ModuleSettings["ListTitle"].ToString();

   23 }

To store settings for your module’s instance, you must write code inside the UpdateSettings() method. You store values by creating a ModuleController object and calling its UpdateModuleSetting() method.

    1 public override void UpdateSettings()

    2 {

    3    ModuleController modController = new ModuleController();

    4    modController.UpdateModuleSetting(ModuleId,

    5         "Mode",ModeDropDownList.SelectedValue);

    6    modController.UpdateModuleSetting(ModuleId,

    7         "ShowPicture", ShowPictureCheckBox.Checked.ToString());

    8    modController.UpdateModuleSetting(ModuleId,

    9         "ListTitle", TitleTextBox.Text.Trim());

   10 }

 

To Summarize

By using these essentials, you can start making your modules do some amazing things.

  1. Changing a page's title and <head> meta-data is as easy as casting your Page object to a DotNetNuke.CDefault object.
  2. By creating a custom URL Rewrite you can have one instance of your module display different information for different URLs. Place your rule inside the SiteURLs.config file and use the Request.QueryString associative array to access the key to the record in your database that you wish to display.
  3. Inside any of your code that subclasses the PortalModuleBase class you can retrieve modules settings through the Settings property. Inside code that subclasses the ModuleSettings class you can retrieve module settings through the ModuleSettings property. To store settings for a module you must create an instance of the ModuleController class.

Part III of this series will be here soon! Bookmark this page or subscribe to the blog feed to be notified when it arrives.

 

Thanks for reading!

 

On Wednesday I wrote about how the current DotNetNuke documentation does a poor job of bridging the gap between writing very basic modules and writing very functional modules: The Basics Are Just Not Enough When Implementing A DotNetNuke Module.

Today I am starting a series of articles aimed at helping you begin writing more functional modules. This is Part I of the Making Your DotNetNuke Module Do More For You series.

In this article I will demonstrate how you can begin to use your DotNetNuke module’s Data Access Layer (DAL) to achieve certain functionality when you need it. After reading this you should be able to add code to your DataProvider class that will allow you to retrieve output parameter values from a stored procedure and then pass those values up into your business logic layer.

Before you begin, make certain that you have your DotNetNuke basics down. At a bare minimum you need to understand how the presentation, business logic, and data layers are divided within the DotNetNuke architecture. It is essential that you be able to identify which files in your code represent each layer of the architecture. This information is covered in the third section of Michael Washington’s module development guide. You can download that document from the DotNetNuke website.

Let’s begin.

The Basic DAL / BLL Functionality

Assuming you follow the prescribed methodologies (and not using DAL+, an article for another day) your module likely executes almost identically to this:

Your presentation layer code-behind gets data by calling one of your controller’s methods:

    1 string criteria = SearcTextBox.Text.Trim();

    2 JimmyHaHaController jhhController = new JimmyHaHaController();

    3 List<JimmyHaHaInfo> searchResults =

    4     jhhController.SearchJimmyHaHas(criteria);

    5 SearchResultsDataList.DataSource = searchResults;

    6 SearchResultsDataList.DataBind();

Inside your controller’s method you use the CBO.FillCollection method to convert your database query result records into a List of objects that represent your data in a meaningful way. As a parameter to the FillCollection method, you pass the results of a query you call on your DataProvider. 

    1 public List<JimmyHaHaInfo> SearchJimmyHaHas(string Criteria)

    2 {

    3     return CBO.FillCollection<JimmyHaHaInfo>(

    4         DataProvider.Instance().SearchJimmyHaHas(Criteria));

    5 }

Finally, inside the method you called on your DataProvider, you execute some code that performs the database query and returns a DataReader.

    1 public override IDataReader SearchJimmyHaHas(string Criteria)

    2 {

    3     return (IDataReader)SqlHelper.ExecuteReader(

    4         ConnectionString,

    5         GetFullyQualifiedName("SearchJimmyHaHas"),

    6         Criteria);

    7 }

This architecture is tried and proven, and so I recommend you stick with it unless you have a good reason not to.

But now let’s consider a real-life module requirement that you might run into.

You need your module to query a database and search for records in a table that match certain criteria. You will present the search results on a Data List control. However, for efficiency reasons, you only want to pull 10 records from the database at once to show on a “page” of results. In the same query you will also need to retrieve a scalar value, the total number of search results.

In the presentation layer you will use this combination to efficiently implement paging on your Data List control:

I won’t get into the minutiae of stored procedures, but here is an example that does what we described above:

    1 ALTER PROCEDURE dbo.SearchJimmyHaHas

    2     (

    3     @Criteria varchar(200),

    4     @PageIndex int,

    5     @NumRows int,

    6     @ResultCount int output

    7     )

    8 

    9 AS

   10 

   11 BEGIN

   12 

   13     /* GET TOTAL NUMBER OF SEARCH RESULTS */

   14     SELECT @ResultCount=(SELECT COUNT(*)

   15         FROM haha JOIN FREETEXTTABLE(haha, *, @Criteria)ftt

   16         on haha.id=ftt.[KEY] )

   17 

   18     Declare @StartRowIndex INT;

   19     set @StartRowIndex = (@PageIndex * @NumRows) + 1;

   20 

   21     With ItemMatches as (

   22 

   23         SELECT

   24             ROW_NUMBER() OVER (ORDER BY ftt.rank DESC) as Row,

   25             ftt.rank,

   26             haha.id,

   27             haha.title

   28         from haha

   29         join freetexttable(haha, *, @Criteria) ftt

   30         on haha.id=ftt.[KEY]

   31     )

   32 

   33     SELECT

   34         rank,

   35         id,

   36         title

   37     FROM ItemMatches

   38     WHERE Row between

   39     @StartRowIndex and @StartRowIndex+@NumRows-1

   40 

   41 END

In a nutshell, the stored procedure takes four parameters, the first three as input parameters, and the final one as an output parameter. This final parameter gets set to the total number of search results. The stored procedure itself only returns at most the quantity of rows specified in the @NumRows parameter of the stored procedure.

Once you start inquiring about how to get the value of the output parameter, it becomes apparent that your current code doesn’t contain the facilities to accomplish this, meaning you’re going to have to start thinking about changing some code inside your DAL and BLL.

There are several approaches you can take here to acquire the value of the output parameter from the stored procedure. One appropriate solution might involve removing the CBO hydrator from your BLL, and implementing your own. This would give you full control over the traversal of data using the DataReader from your DataProvider.

Fortunately, you won’t need to take this route unless you are wanting to achieve other goals such as improving the performance of the hydrating process, which again is a topic for another day.

The approach we will take in this tutorial is to leave the existing data architecture in place. So let’s take a look at the two parts of code that we have to work with.

First we have the DataProvider method that calls the stored procedure and returns a SqlDataReader back to the controller object that called the method.

    1 public override IDataReader SearchJimmyHaHas(string Criteria)

    2 {

    3     return (IDataReader)SqlHelper.ExecuteReader(

    4         ConnectionString,

    5         GetFullyQualifiedName("SearchJimmyHaHas"),

    6         Criteria);

    7 }

The results of this method get passed on to the second region of code that concerns us, the CBO.FillCollection() hydration method that is called inside our controller’s code. The FillCollection() method requires the List<> data type as a parameter. Thus we want to keep the same return type on our DataProvider’s method. All other code in our DataProvider method is fair game for changing. And nothing is stopping us from doing so.

Allow me to present you with one implementation of this method that solves the problem:

    1 public override IDataReader SearchJimmyHaHas(

    2     string SearchCriteria,

    3     int PageIndex,

    4     int NumRows,

    5     out SqlParameter ResultParam)

    6 {

    7     SqlConnection connection =

    8         new SqlConnection(ConnectionString);

    9     SqlCommand cmd =

   10         new SqlCommand("SearchJimmyHaHas", connection);

   11     cmd.CommandType = CommandType.StoredProcedure;

   12     cmd.Parameters.Add(

   13         new SqlParameter("@Criteria",SearchCriteria));

   14     cmd.Parameters.Add(

   15         new SqlParameter("@PageIndex", PageIndex));

   16     cmd.Parameters.Add(new SqlParameter("@NumRows", NumRows));

   17     ResultParam = new SqlParameter();

   18     ResultParam.ParameterName = "@ResultCount";

   19     cmd.Parameters.Add(ResultParam);

   20     cmd.Parameters["@ResultCount"].SqlDbType = SqlDbType.Int;

   21     cmd.Parameters["@ResultCount"].Direction

   22         = ParameterDirection.Output;

   23     connection.Open();

   24     return cmd.ExecuteReader();

   25 }

Starting at the method signature, you see that I added a new parameter that uses the out keyword to make it a reference parameter. This will allow the value of the output parameter from the stored procedure to be accessed back inside the controller object.

Next you see that the call to the SqlHelper.ExecuteReader() method has been replaced by a few lines of code that allow us to explicitly configure the parameters that go into the SqlCommand object. Here we’ve taken control of the query details by implementing them ourself rather than letting the SqlHelper object do it for us.

Let me warn you that the steps above were difficult to get working correctly. There appears to be a bug in the way SqlCommand and SqlParameter interact with stored procedure output. In my experience you need to add the output parameter to the command first. After that you can configure the direction and db type. The other parameters can be added normally using one of the many convenient Parameter.Add overloads. So if you have problems, try mimicking the code I provided here.

Since we changed the parameters to the Data Provider method, this changes the way our controller code must call the method.

The method requires a SqlParameter object to be passed in by reference using the out keyword. So we create an instance of SqlParameter and do just that. Then we call the CBO.FillCollection object like usual. Instead of directly returning it, we save a reference to the results so that we can retrieve the stored procedure output parameter we care so much about.

A second caveat that I must mention here is that the stored procedure output parameter value must be accessed AFTER the FillCollection() method is executed. This is due to the way Readers function. When using a Reader object, the output parameter values from a stored procedure are not available until after every record in the query results has been traversed, and the Reader is closed. That is precicely what takes place inside the FillCollection() call. If you take a minute to think about it, it doesn’t even make sense to try to access the output value before at least calling your DataProvider method, because no data whatsoever is available until you do so.  Just keep this in mind when you are modifying your own Data Access Layer.

The final thing you will notice is that I added and extra parameter to the BLL’s method to allow the returned value to be passed up into the presentation layer.

    1 public List<JimmyHaHaInfo> SearchJimmyHaHas(

    2     string SearchCriteria,

    3     int PageIndex,

    4     int NumRows,

    5     out int ResultCount)

    6 {

    7     SqlParameter param = new SqlParameter();

    8     List<JimmyHaHaInfo> list =

    9         CBO.FillCollection<JimmyHaHaInfo>(

   10             DataProvider.Instance().SearchJimmyHaHas(

   11                 SearchCriteria,

   12                 PageIndex,

   13                 NumRows,

   14                 out param));

   15     ResultCount = (int)param.Value;

   16     return list;

   17 }

There are numerous ways to skin this cat, but I simply am keeping with the theme of using the reference parameter in this tutorial.

To Summarize

This is only one example of how you can start customizing parts of your modules’ BLL and DAL. With any luck the take-away points that you get from today’s article are these:

  1. Common-day module solutions have special requirements that necessitate that you modify the code that accesses your database. If you haven’t done so already, you should start becoming familiar with customizing your DataProvider code and your BLL code.
  2. There is always more than one way to accomplish what you need within your code. The more familiar you are with the functionality of DotNetNuke, the more likely it will be that you can accomplish your goals without having to re-write much of the functionality that the DotNetNuke core provides.
  3.  For example, you can retrieve the values of stored procedure output parameters by making the small changes described above.
  4. Moreover, reference parameters can be used to pass data between the layers in your module.

You have been reading the first installment in my series on making your DotNetNuke module do more for you.  In the next installment I will be discussing how the DotNetNuke page can impact the functionality of your module, and how your module can modify parts of the page it lives on. I will see you then!

Update: Click here to read Part II: Common DotNetNuke Module Page Tasks

Implementing the recommended DotNetNuke design patterns within your custom module can be painful if you are unfamiliar some basic, yet empirical DotNetNuke programming techniques.

Anyone who has studied Michael Washington’s Module Developer Guides can probably testify that although these instructions can be a good introduction into creating your very first, basic DotNetNuke module, they don’t exactly advance the beginning module developer into implementing functionality that requires moderate experience with DotNetNuke as well as the .Net Framework.

I have read all the documentation I can get my hands on, including the 2007 published For Dummies book, Shaun Walker & Co.’s Professional DotNetNuke 4.0 (which by the way isn’t half bad), and Symmond’s DNN Website Creation book from 2006. Some of these are better than others, but they all have one thing in common. Their simplicity and focus on the bare basics always leads to fundamental information being left out. This leaves beginners in the dark during their most vulnerable moments!

That said, I am highly anticipating the upcoming Wrox publication, DotNetNuke Module Programming by Chris Paterra and Shaun Walker. Hopefully it will fill in some of the holes left open by previous books and offer more insightful information for the intermediate module developer. A look at the book’s description doesn’t convince me that it will deliver either of these; however, if either of the book’s authors happen to read this, I would be thrilled to be proven wrong.  I also would gladly accept an advance copy of your title!

But back to my main topic.

It is my personal opinion that there is a void of information for beginning module developers that want to take a step beyond the basic tutorials. In the coming weeks I will be posting some relatively simple examples that will demonstrate some common programming necessities that are not covered within the publications I have read. These examples are intended to help bridge the gap between writing very basic modules and writing very functional modules. I hope many of you find my articles useful!

If you are interested in following me along on this journey, please bookmark this site, subscribe to the feed, or join my monthly mailing list.

Update: The first installment of my series on Making Your DotNetNuke Module Do More For You is now available here:

Part I: Utilizing Your DotNetNuke Module's Data Access Layer

Who Is Rafe

rafe

I Am Rafe Kemmis

I am an audacious entrepreneur with a double bachelor of science in Computer Science and Mathematics. I specialize in Microsoft ASP.Net web application development as well as accredited information systems auditing.

Questions?

Always a thoughtful response. You may post your question on an article, or contact me directly.

Hire Me.

I provide custom solutions to complex problems. I can help your business no matter how large or small.

Contact me now.

Subscribe