When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips

Sections:
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
XML Info
Information:
Feedback
Author an Article
ASP ASP.NET ASP FAQs Message Board Feedback
Print this page.
Published: Friday, May 21, 2004

Return to the Building Websites with the ASP.Net Community Starter Kit Sample Chapter Page
Return to the Complete Listing of Sample Chapters

Initializing the FAQ Module

Every community module has a corresponding maintenance stored procedure to populate the database with settings required for the module to work. Specifically, we need to register content page types by inserting two records into Community_PageTypes: one for an FAQ section page (to display a list of FAQs) and one for an FAQ page (showing a single FAQ in detail). We call the procedure Community_MaintenanceInitializeFaqs, following the existing CSK naming convention. An excerpt registering the FAQ section page type is shown here:

IF NOT EXISTS (SELECT * FROM Community_PageTypes WHERE pageType_Name='Faq Section')
BEGIN
INSERT Community_PageTypes
(
pageType_name,
pageType_description,
pageType_pageContent,
pageType_IsSectionType,
pageType_ServiceSelect
)
VALUES
(
'FAQ Section',
'Contains FAQs in a question and answer style format',
'ASPNET.StarterKit.Communities.Faqs.FaqSection',
1,
'Community_FaqsServiceSelect'
)
END
ELSE
PRINT 'WARNING: The FAQ Module has already been registered.'

The CSK caches data from Community_NamePages so as to retrieve the data only once. If you make modifications to the table, you'll need to restart the web application for the changes to take effect in the CSK.

The maintenance stored procedure also needs to register the named pages (static content) for the new module. Named pages for the FAQ section will include the page to add an FAQ and a page to edit an FAQ. You'll have to choose your page names at this point and use the same names later when you create the ASPX file.

Here's an excerpt from Community_MaintenanceInitializeFaqs to add a named page for adding FAQs:

IF NOT EXISTS (SELECT * FROM Community_NamedPages WHERE namedPage_Path='/Faqs_AddFaq.aspx')
BEGIN
INSERT Community_NamedPages
(
namedPage_name,
namedPage_path,
namedPage_pageContent,
namedPage_title,
namedPage_description,
namedPage_sortOrder,
namedPage_isVisible,
namedPage_menuID
)
VALUES
(
'AddFaq',
'/Faqs_AddFaq.aspx',
'ASPNET.StarterKit.Communities.Faqs.AddFaq',
'Add FAQ',
'Enables users to add a new FAQ',
0,
1,
0
)
END
ELSE
PRINT 'WARNING: /Faqs_AddFaq.aspx has already been registered as a NamedPage.'

The namedPage_pageContent parameter is the name of the class that the CSK will instantiate as the code-behind logic for the page. The name includes the full namespace qualifier ASPNET.StarterKit.Communities.Faqs.AddFaq.

The maintenance stored procedure needs to execute during the database setup. We will take a look at how to do this in Chapter 11.

FAQ Components

The C# code for our FAQ module will reside in the Engine\Modules\Faqs directory. First, we will write out helper components and place these in a Components directory. Each module in the CSK places components inside a distinct namespace below ASPNET.StarterKit.Communities, and the existing modules use the name of the module as the additional namespace qualifier (Faqs).

FaqInfo

FaqInfo class extends the ContentInfo class to offer data properties specific to an FAQ. The code for this class is shown as follows:

using System;
using System.Data.SqlClient;
namespace ASPNET.StarterKit.Communities.Faqs
{
public class FaqInfo : ContentInfo
{
public FaqInfo(SqlDataReader dr) : base(dr)
{
if(dr["Faq_Answer"] != DBNull.Value)
{
_answerText = (string)dr["Faq_Answer"];
}
if(dr["Faq_Reference"] != DBNull.Value)
{
_referenceText = (string)dr["Faq_Reference"];
}
}
public string AnswerText
{
get { return _answerText; }
set { _answerText = value; }
}
public string ReferenceText
{
get { return _referenceText; }
set { _referenceText = value; }
}
public string QuestionText
{
get { return base.Title; }
set { base.Title = value; }
}
public string IntroText
{
get { return base.BriefDescription; }
set { base.BriefDescription = value; }
}
private string _answerText;
private string _referenceText;
}
}

FaqInfo expects initialization with an instance of the SqlDataReader class. We will be writing the data-access code to create a SqlDataReader in our next class.

FaqUtility

Following the patterns set forth in the rest of the CSK, we will put all of our data-access routines into static methods of a utility class. There should be one static method available for each of the FAQ-related stored procedures (with the exception of the maintenance stored procedure, which we should not need to invoke during regular operations of the community site but only during setup). Each of these routines will need to map incoming variables to stored procedure parameters and execute the procedure.

Here's the AddFaq method:

public static int AddFaq(  
string username,
int sectionID,
int topicID,
string question,
string introduction,
string answer,
string reference,
int moderationStatus)
{
SqlConnection conPortal = new SqlConnection(
CommunityGlobals.ConnectionString);
SqlCommand cmdAdd = new SqlCommand(
"Community_FaqsAddFaq", conPortal);
cmdAdd.CommandType = CommandType.StoredProcedure;
cmdAdd.Parameters.Add("@RETURN_VALUE",
SqlDbType.Int).Direction =
ParameterDirection.ReturnValue;
cmdAdd.Parameters.Add("@communityID",
CommunityGlobals.CommunityID);
cmdAdd.Parameters.Add("@sectionID", sectionID);
cmdAdd.Parameters.Add("@username", username);
cmdAdd.Parameters.Add("@topicID", topicID);
cmdAdd.Parameters.Add("@question", question);
cmdAdd.Parameters.Add("@introduction", introduction);
cmdAdd.Parameters.Add("@metaDescription",
ContentPageUtility.CalculateMetaDescription(introduction));
cmdAdd.Parameters.Add("@metaKeys",
ContentPageUtility.CalculateMetaKeys(introduction));
cmdAdd.Parameters.Add("@moderationStatus", moderationStatus );
cmdAdd.Parameters.Add("@answer", SqlDbType.NText);
cmdAdd.Parameters.Add("@reference", SqlDbType.NText);
cmdAdd.Parameters["@answer"].Value = answer;
cmdAdd.Parameters["@reference"].Value = reference;

conPortal.Open();
cmdAdd.ExecuteNonQuery();
int result = (int)cmdAdd.Parameters["@RETURN_VALUE"].Value;
SearchUtility.AddSearchKeys(conPortal, sectionID, result,
question, introduction);
conPortal.Close();
return result;
}

Notice that the AddFaq method also generates the search keys for the content using the SearchUtility class, and the newly created identifier of the content returned by the stored procedure we reviewed earlier.

The EditFaq method almost duplicates the AddFaq method except for calling a different stored procedure and using EditSearchKeys on the SearchUtility class to update the FAQ search keys.

One improvement you might consider making to the CSK is adding a try catch finally statement to ensure the database connection will always invoke the Close method, even in the face of an exception. The chances of an exception are small, but on a high volume community site, you cannot afford the opportunity to waste database connections.

The other two methods in FaqUtility are GetFaqs and GetFaqInfo. GetFaqs loops through records in a SqlDataReader to return an ArrayList of FaqInfo objects, while GetFaqInfo expects only a single record in the database results and returns a single new FaqInfo object. These two methods from the class are shown here:

public static ContentInfo GetFaqInfo(string username, int contentPageID)
{
FaqInfo faq = null;
SqlConnection conPortal = new SqlConnection(
CommunityGlobals.ConnectionString);
SqlCommand cmdGet = new SqlCommand(
"Community_FaqsGetFaq", conPortal);
cmdGet.CommandType = CommandType.StoredProcedure;
cmdGet.Parameters.Add(
"@communityID", CommunityGlobals.CommunityID);
cmdGet.Parameters.Add("@username", username);
cmdGet.Parameters.Add("@contentPageID", contentPageID);
conPortal.Open();
SqlDataReader dr = cmdGet.ExecuteReader();
if (dr.Read())
faq = new FaqInfo(dr);
conPortal.Close();
return faq;
}
public static ArrayList GetFaqs(string username, int sectionID,
int pageSize, int pageIndex, string sortOrder)
{
SqlConnection conPortal = new SqlConnection(CommunityGlobals.ConnectionString);
SqlCommand cmdGet = new SqlCommand( "Community_FaqsGetFaqs", conPortal);
cmdGet.CommandType = CommandType.StoredProcedure;
cmdGet.Parameters.Add("@communityID",
CommunityGlobals.CommunityID);
cmdGet.Parameters.Add("@username", username);
cmdGet.Parameters.Add("@sectionID", sectionID);
cmdGet.Parameters.Add("@pageSize", pageSize);
cmdGet.Parameters.Add("@pageIndex", pageIndex);
cmdGet.Parameters.Add("@sortOrder", sortOrder);

ArrayList faqs = new ArrayList();
conPortal.Open();
SqlDataReader dr = cmdGet.ExecuteReader();
while (dr.Read())
faqs.Add(new FaqInfo(dr));
conPortal.Close();
return faqs;
}

It is important for GetFaqInfo to use the return value and parameter list shown above. The framework should invoke these methods through a delegate and the signatures must match. We will see how this works when we write the content pages.

Our data-access layer is now complete. If you build a module in this fashion, you should be able to compile the solution at this time to resolve any errors. You might consider writing a driver page to exercise the four static methods in FaqUtility and verify the results by looking in the Community_Faqs and Community_ContentPages tables of the database.

  • Read Part 4!

    Buy this Book!
    Want to read the entire book? Pick up a copy of Building Websites with the ASP.Net Community Starter Kit from Amazon.com today!


  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article