Manage Connection Strings In Multiple Environments

Have you ever had to move or deploy applications to different servers and forgotten to change connection strings settings? Maybe you were developing against a local database and when you push to production your web.config (or wherever you store your connection strings) stayed pointing to your local instead of production. Another common pain is when moving an application throughout the various stages of the software development lifecycle; perhaps from a development environment, to testing server(s), staging server(s), and finally to production? In multi-environment development, deployment can become a real headache.

There are several different approaches to making this easier, including creating post build scripts, creating a deployment project (which I will probably cover in a future blog post), and simply implementing manual processes to follow. However, I’ve found in my own recreational programming, that creating a ConnectionManagement class is a pretty slick and easy way to go that garners all kinds of added benefits.

A Two Environment Example

Let’s take the example of developing against a local SQL database (or database project), and deploying directly to production; something which is typically done in very small projects.

We want to create a single class that we statically call to retrieve the appropriate connection string, but still maintain the ability to house the connection strings inside an easily configurable file (such as web.config). In this example, we’re not particularly concerned if the seperate environments have knowledge of the other environments’ connection strings (this is a potential security risk if you have several developers who have access to web.config via dev server/test server and should not have access to production server, though you can encrypt or take additional measures to protect the connection strings).

I create this class in my DataAccess project where the rest of my data logic resides. Let’s set this class up like the following:

ConnectionManager Class

public class ConnectionManager
    {
        public static string GetConnectionString()
        {
            string appMode = string.Empty;
            if (ConfigurationSettings.AppSettings["Mode"] != null)
            {
                appMode = ConfigurationSettings.AppSettings["Mode"].ToLower();
            }

            if (appMode == "prd")
            {
                return ConfigurationManager.ConnectionStrings["MyDBPrd"].ConnectionString;
            }
            else
            {
                return ConfigurationManager.ConnectionStrings["MyDBDev"].ConnectionString;
            }
        }
    }

Make sure you’ve added using System.Configuration; to the top of your class and if necessary added to your class library/project.

What we are doing here is very simple; we’re just going to get the appropriate connection string based on an app setting found in the web.config of the calling project. If the mode is “prd” (I know, I’m using hardcoded strings here…tisk, tisk) then return production, if it’s anything else return the dev connection string.

Web.Config of Consuming Project

Let’s take a look at the web.config app setting we’ve added to control the connection strings.

<appSettings>
      <!-- Possible values are: Dev, Prd-->
      <add key="Mode" value="Dev"/>
</appSettings>

App.Config of DataAccess Layer

Inside the data access layer, where the connection manager class resides, I have an app.config which houses all of the connection string information. If you don’t use multi-tiered design, this could go in your calling project (webapp?) instead.

<connectionStrings>
    <add name="MyDBDev" connectionString="Data Source=MY-PC\SQLEXPRESS;Initial Catalog=MyDB;Integrated Security=True"/>
    <add name="MyDBPrd" connectionString="server=localhost;uid=app;pwd=mysecurepassword;database=MyDB;application name=MyApp;Max pool Size=500;Connect Timeout=30;connection reset=false;connection lifetime=5;"/>
</connectionStrings>

Calling Our New ConnectionManager.GetConnectionString() Function

Ok, now that we went to all this trouble, make sure everywhere we call a connection string (which should probably only be in our data access layer), we call this method, like so:

Linq Example

public static Specy GetSpecies(int SpeciesID)
{
	//This next line creates a data context from linq and uses the ConnectionManager.GetConnectionString()
	//function to get the connection string it uses.  Even though this Linq2SQL data context has a connection
	//string associated with it, i am setting it in case we change environments.
	MasterDataContext dc = new MasterDataContext(ConnectionManager.GetConnectionString());
        var query = from a in dc.Species
        	where a.SpeciesID == SpeciesID
                select a;

        return query.SingleOrDefault();
}

Traditional ADO.NET Method

public static DataTable GetControlSpecies()
{
	//This next line creates a sql connection object and uses the ConnectionManager.GetConnectionString()
	//function to get the connection string it uses.  Using the same usage everytime and letting the
	//connection manager dictate which connection based on environment consolidates your code and allows you
	//to deploy your web applications to different database environments quickly and easily.
	using (SqlConnection connection = new SqlConnection(ConnectionManager.GetConnectionString()))
        {
        	string sql = "SELECT SpeciesID, CommonName, ScientificName FROM Species ORDER BY CommonName ASC";

                var cmd = new SqlCommand(sql, connection);

                var da = new SqlDataAdapter(cmd);
                var dt = new DataTable();
                da.Fill(dt);

                return dt;
	}

}

When you deploy to an environment now you only need to ensure one value is set correctly (the app setting Mode property we created)! And most of the time, if you’re like me, you hardly ever change the web.config after initial setup anyway; so you’re all set!

Dealing With Several Environments

In this case we only have two environments, the local copy we develop against, and production…However, what if we have several environments, or several different databases. To expand on this basic class, we can add a switch/case statement to jump through all of the possible combinations of environments such as the following:

public class ConnectionManager
    {
        public static string GetConnectionString()
        {
            string appMode = string.Empty;
            if (ConfigurationSettings.AppSettings["Mode"] != null)
            {
                appMode = ConfigurationSettings.AppSettings["Mode"].ToLower();
            }

	    string connectionString = string.Empty;
            switch(appMode)
            {
                case "prd": //Production
			connectionString = ConfigurationManager.ConnectionStrings["MyDBPrd"].ConnectionString;
			break;
		case "dev": //Development
			connectionString = ConfigurationManager.ConnectionStrings["MyDBDev"].ConnectionString;
			break;
		case "tst": //Test
			connectionString = ConfigurationManager.ConnectionStrings["MyDBTest"].ConnectionString;
			break;
		case "stg": //Staging
			connectionString = ConfigurationManager.ConnectionStrings["MyDBStaging"].ConnectionString;
			break;
		case "vendor": //Vendors
			connectionString = ConfigurationManager.ConnectionStrings["MyDBVendor"].ConnectionString;
			break;
		case "local":  //Local
			connectionString = ConfigurationManager.ConnectionStrings["MyDBLocal"].ConnectionString;
			break;
	    }

	    return connectionString;
        }
    }

Expanding on the ConnectionManager Class

Having a centralized point of entry for accessing your connection strings is helpful for other reasons as well. Perhaps you need to add custom encryption/decryption to your connection strings, or control access to specific connection strings programmatically. These, as well as many additional future enhancements can be built into your class and trickled down to all consuming calls.

One final note, it may also be helpful to create an enum of different databases to pass as a parameter to the ConnectionManager.GetConnectionString() function such as ConfigurationManager.GetConnectionString(Databases.Users) or ConfigurationManager.GetConnectionString(Databases.Transactions), etc. to use the class to access specific databases on a single server.

Thanks for reading, and hope you find this useful! Please feel free to add any comments or suggestions on managing connection strings!

Creating a Thick Entity Class Template

Templates in Visual Studio are a great way to promote consistency as well as take some of the chore out of common layout when creating items or projects. Creating templates
is easy to do in Visual Studio; you can get started by simply converting existing classes by clicking File > Export Template, and from there follow the wizard. Another option, to gain maximum flexibility, is to create the template by simply using a basic text editor (even Notepad works fine for this).

You can view several pre-existing templates that come pre-built with Visual Studio found in the
ItemTemplate and ProjectTemplate directories (My paths are: C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ItemTemplates
and C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ProjectTemplates,
respectively). The templates are zip files that generally contain three files, your template code (.cs), a template configuration file, and the icon image for the template.

What is a Thick Entity Class?

A thick entity is a class that describes an entity object which also includes, or is at least knowledgeable of business logic as well. A thin entity would be an entity object which only describes the properties of the object (much like an interface) and does not contain methods/functions. There are pro’s and con’s to both which is an entire discussion outside of the scope of this particular article.

The following is a template for creating thick entity classes, or you can download the thick entity class template here and simply copy it to your Visual Studio user template directory (I would place mine here: My Documents\Visual Studio 2008\Templates\ItemTemplates\Visual C#).

ThickEntity.cs

using System;
using System.Collections.Generic;
$if$ ($targetframeworkversion$ == 3.5)using System.Linq;
$endif$using System.Text;

namespace $rootnamespace$
{
    public class $safeitemrootname$
    {
        #region "PROPERTIES"

        /// <summary>
        /// Unique identifier for the $safeitemrootname$.
        /// </summary>
        public int ID { get; set; }

        //TODO: Add additional entity properties

        #endregion

        #region "CONSTRUCTORS"

        /// <summary>
        /// Create an $safeitemrootname$ in memory.
        /// </summary>
        public $safeitemrootname$()
        {
        }

        #endregion

        #region "METHODS"

        /// <summary>
        /// Load existing $safeitemrootname$ from database.
        /// </summary>
        /// <param name="id"></param>
        public void LoadData(int id)
        {
            //TODO: Load from database
        }

	//Add any additional load mechanisms/helpers


        /// <summary>
        /// Save $safeitemrootname$ to database.
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public bool Save(ref string msg)
        {
            bool result = false;

            //Validate for good input data
            result = ValidateData(ref msg);
            if (result == false)
            {
                return false;
            }

            //Reset result after clears validation
            result = false;

            //Save to database
            try
            {
                if (this.ID > 0)
                {
                    //TODO: Call update function
                }
                else
                {
                    //TODO: Call create function

                    if (this.ID > 0)
                    {
                        result = true;
                    }
                    else
                    {
                        result = false;
                    }
                }
            }
            catch (Exception ex)
            {
                //TODO: Log exception
            }

            if (result == false && string.IsNullOrEmpty(msg))
            {
                //Catch-all generic error
                msg = "An error ocurred while saving.";
            }

            return result;
        }

        /// <summary>
        /// Validate data for $safeitemrootname$.
        /// </summary>
        /// <returns></returns>
        public bool ValidateData()
        {
            string msg = string.Empty;
            bool isValid = ValidateDataHelper(ref msg);
            return isValid;
        }

        /// <summary>
        /// Validate data for $safeitemrootname$.
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public bool ValidateData(ref string msg)
        {
            bool isValid = ValidateDataHelper(ref msg);
            return isValid;
        }

        /// <summary>
        /// Validate data for $safeitemrootname$.
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private bool ValidateDataHelper(ref string msg)
        {
            StringBuilder sb = new StringBuilder();
            bool isValid = true;

            //TODO: Add validation logic

            if (string.IsNullOrEmpty(sb.ToString()) == false)
            {
                sb.Insert(0, "Oops, some of the data you entered needs attention:");
                msg = sb.ToString();
            }
            else
            {
                msg = string.Empty;
            }

            return isValid;
        }

        #endregion

        #region "STATIC METHODS"

        /// <summary>
        /// Get complete list of $safeitemrootname$s.
        /// </summary>
        /// <returns></returns>
        public static List<$safeitemrootname$> Get$safeitemrootname$s()
        {
            var coll = new List<$safeitemrootname$<();
            
	    //TODO: Load list from data access layer

	    //Example:
	    //var dataColl = $rootnamespace$.DataAccess.Get$safeitemrootname$s();
            //foreach (dataIssue dataItem in dataColl)
            //{
            //    Issue issue = new Issue();
            //    issue.LoadData(linqItem);
            //    coll.Add(issue);
            //}

            return coll;
        }

        #endregion
    }
}

MyTemplate.vstemplate

<VSTemplate Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Item">
  <TemplateData>
    <DefaultName>ThickEntity.cs</DefaultName>
    <Name>ThickEntity</Name>
    <Description>Create a thick (contains business logic) entity class.</Description>
    <ProjectType>CSharp</ProjectType>
    <SortOrder>10</SortOrder>
    <Icon>__TemplateIcon.ico</Icon>
  </TemplateData>
  <TemplateContent>
    <References />
    <ProjectItem SubType="Code" TargetFileName="$fileinputname$.cs" ReplaceParameters="true">ThickEntity.cs</ProjectItem>
  </TemplateContent>
</VSTemplate>

Do you have a collection of useful templates that you would like to share? I would love to share, discuss, and add your templates to my collection. Please contact me or leave code samples directly in the comments.

It’s The Little Things That Make The Difference

Constructive Criticism

Please indulge me for a minute… you’ve just completed a huge launch of a new e-commerce website. The website does everything; all the bells and whistles of a multi-language, multi-currency, real-time shipping, dynamic inventory, dropshipping, customer portal, full administration, on and on…

You’re feeling pretty good, knowing that no where else can you get everything that you’ve packed into this online presence. Your happy tester/client/customer/fellow developer takes one look at it and says the following:

“Where’s the little icon in the address bar? Why don’t I see it on Google? How much traffic does the website have yet? Can you give me a report on signups, traffic, and other minutia? I changed one of the page names and it says file not found, could it show something friendlier? It doesn’t look right on my Mac with Safari.”

Man, all that hard work, and what’s the response; criticism. Well, get over it. They’re right, you’re wrong. As much as that sucks some times, remember that criticism from colleagues is almost always constructive. You should always be happy that somenoe cares enough to give you feedback. So take the feedback, roll up your sleeves and fix the little things that really make the project, it’s just part of the process.

I’ve always noticed that as cynical as it is, the things you do wrong always garner the most attention, so put some time into the details and try and avoid little mistakes so people can focus on how amazing this application is.

Finishing Tips

Here’s a few tips that I try and remember for every project, perhaps it might help you too:

Meta Information such as keywords, description, and author. Meta information is not dead, even Google uses meta information for search criteria as evidenced by their webmasters section which describes errors found in your crawl statistics referring to missing or duplicate meta information across pages.

Titles. It looks a bit silly having a dynamite page with an Untitled bar at the top of the browser. Not only does it display to the user a proper title for your website/application, but it most likely helps SEO as well.

Sitemaps, both in XML and as user facing pages. Sitemaps provide search engines such as Google and Yahoo quicker information for page indexing which allows these search engines to crawl your website more frequently. User facing sitemaps do actually help some users navigate to the information they’re after. Check out a search for Google Sitemap Generator, where you can find services that generate XML sitemaps for you.

Favicon, yes this is “cutesy”, but remember that brand identity is what advertising agencies get paid millions for! A simple icon can help give visual connections to your users that could make the difference in them finding and remembering you again. You can find favicon generators which convert your uploaded image to a favicon.ico to place on your website.

Robots.txt; Google recommends it, I do it. A robots.txt file helps tell crawling bots where not to look. This free’s up search engine resources by limiting their crawls of non-indexable or unimportant data. A good rule of thumb is, if you don’t want a user to get to a certain directory, throw it in the robots.txt file. Find out more information on robots.txt.

IIS Error Pages (or ASP.NET error pages) provide a way to clean up errors or unexpected behavior. Errors happen, but when they do, it would be a lot nicer if the user sees what’s going on in a friendly, explained way, with similar navigation, branding, and look and feel as the rest of the website. Add pages such as:

  • error.htm (catch-all for non-specified errors)
  • noaccess.htm (when a user requests a resource they do not have access to)
  • notfound.htm (404 errors)
  • etc.

You can wire these up in IIS, or for ASP.NET applications, in web.config (though I would do both personally).

Analytics. Invariably, someone will request some type of benchmarking of how successful the website/application is, and this generally comes in the form of analytics. If no one is asking, then maybe you should be the one. Google Analytics is a good, quick, and easy implementation of analytics for most businesses. However, if you want to inhouse the data, there are several other solutions you can go with. (Webtrends is a pretty large one).

This One is so Important, It Get’s It’s Own Heading – Graphics

Lipstick on a pig…Well, in the case of applications (especially web-based for some reason), great graphics really do cover up a lot. A top-tier designer, is extremely hard to find and can be worth their weight in gold. However, the reverse is usually not true; great applications that look like garbage usually don’t get a first look, let alone a second; there just tends to be too much competition. Graphic design is important for inhouse or business applications as well, not just public facing websites. A design that is well liked by the users acquires much quicker acceptance and in some cases even helps create evangelist-style users. It sounds superficial, especially for most of the developers reading this, but graphics are the single most important finishing touch to any application in my opinion.

I’m sure there are a ton of other tips and common gotcha’s that developers forget to do at the end of projects, so please leave a few in the comments and help us all out!

Running Multiple Versions of IE

Have an issue where you need to compare web ui in multiple versions of IE? Not a problem with Tredosoft’s MultipleIE. First, install IE7 to use the most current version as your primary and then download TredoSoft’s MultipleIE and run shelled versions of IE 3.0-6.0. on the same machine.

The solution isn’t necessarily 100%, but it does provide a quick, simply, and free approach to multi-browser testing.

Of course, you may also want to install some additional browers for testing purposes; I’ve added a pretty common lineup for reference:

You can download the latest version of TredoSoft’s MultipleIE here:

http://tredosoft.com/Multiple_IE

Note: Unfortunately, Multiple IE will not work in Windows Vista, a known issue.

Browser Cam

There is also a service (probably several) which provides you a much richer collection of browser/OS permutations for testing called Browser Cam.

This service does cost money (day, month, year subscriptions), but allows you to terminal into OS-specific client machines and test different browsers easily.

Note: I’ve experienced a bit of a delay when using Browser Cam, and occasionally boxes do temporarily go down (or at least appear unavailable) from time to time, so make sure you allocate a bit of testing time aside for using services such as Browser Cam.

No Analytics, No Problem – Go Old School with IIS Log Parsing

Log Parser is a powerful little tool that allows you to write SQL-like queries against your IIS log files. (I somewhat think this was a base for Powershell, but maybe that’s just me). You can download Log Parser from Microsoft.

Below are a few Log Parser scripts that I occasionally use on my own website(s) for double checking hit counts, page views, and top IIS errors occurring in the web application.

This is by far only a tiny sample of what you can do with Log Parser and those of you who are data mining fanatics, you can go nuts compiling information against your IIS log files! Have fun!

Don’t forget to change the path to point to your log file(s) directory for the website/web application you are analyzing.

Get hit count on web-resources:

LOGPARSER “SELECT COUNT(*) as [Count], cs-uri-stem, sc-status from ‘c:\mylogpath\ex*.log’ WHERE sc-status <> 200 GROUP BY cs-uri-stem, sc-status ORDER BY [Count] DESC” -rtp:-1 -o:datagrid

Get page view count on webpages:

LOGPARSER “SELECT Date, COUNT (*) AS PAGEVIEWS FROM ‘c:\mylogpath\ex*.log’ WHERE EXTRACT_EXTENSION(to_lowercase(cs-uri-stem)) NOT IN (‘asf’;’axd’;’css’;’exe’;’gif’;’ico’;’jpg’;’js’;’msi’;’png’;’txt’;’vsi’;’wmv’;’xml’;’zip’) and sc-status=200 Group By Date” -rtp:-1 -o:datagrid

Get unique visitors (by ip address):

logparser “SELECT DISTINCT date, c-ip INTO ‘c:\mylogpath\Temporary.txt’ FROM ‘C:\Program Files\Log Parser 2.2\web_logs\ex*.log'” -o:w3c
logparser “SELECT date, count(c-ip) FROM ‘c:\mylogpath\Temporary.txt’ GROUP BY date” -i:w3c -o:datagrid

Get top IIS errors occurring on website/webapplication:

LOGPARSER “SELECT COUNT(*) as [Count], cs-uri-stem, sc-status from ‘c:\mylogpath\ex*.log’ WHERE sc-status <> 200 GROUP BY cs-uri-stem, sc-status ORDER BY [Count] DESC” -rtp:-1 -o:datagrid

For security-related information on using Log Parser check out this great article from Security Focus.

Homegrown CSS Standards

The following is a brief outline of a few tips, tricks, and personal preferences for standardizing your HTML/CSS websites and web applications. These suggestions have been pulled from personal experience and tend to fit 99% of the projects I’ve been involved in. Remember, even though I’m calling them standards, there are always exception, so don’t simply mimic any set of standards – evaluate the situation and apply the most appropriate solution.

Good Foundations

It is generally easier to begin style implementation with div’s. A CSS-based HTML design typically will have appropriately used div’s to section off major groupings of HTML data. For example:

…
<body>
	<div class=”container”>
		<div class=”header”>
			<h1>My Example HTML</h1>
		</div>
	
		<div class=”nav”>
			<ul>
				<li>Microsoft</li>
				<li>Yahoo</li>
			</ul>
		</div>

		<div class=”content”>
			<h2>My Example Subheader</h2>
			<p>Some lorem ipsum goes here…</p>
			<p>And now a bit more…</p>
		</div>
	</div>
</body>
…

As you can see, there are no tables being used for design purposes, table should generally be used for tabular data and not design layout. HTML with heavily used tables limits the ability for design to be fully manipulated by a CSS style sheet.

Now that we’ve written HTML that is geared to a CSS-based design, we can use the “hooks” we created to manipulate the CSS however we need and it will be readable for both an HTML designer/developer and a CSS developer.

For an example of design being completely dictated by CSS see CSSZenGarden for more. The HTML is always the same and only the CSS file changes in each example.

HTML that is developed in Visual Studio under the default validation settings for HTML will provide warnings/errors of invalid or inappropriate HTML. Please heed these warnings to provide the cleanest amount of HTML to work from. This is the foundation of any web application and for all other aspects such as CSS, JavaScript, and even code-behind to function properly, HTML should be sound. After all warnings have been addressed through Visual Studio, take time to also validate the HTML in an HTML Validator provided by W3 Consortium.

CSS File Organization

When developing a hierarchy of style sheets, separate styles into common and specific categories, then create a common.css file to store all common styles and individual specific CSS files for other major collections of CSS styles. See below example:

If I have a website that consists of three major portals, Public, Vendors, Administration; I would most likely create four CSS style sheets; one for each major portal, as well as one common.css file for common styles used in all portals.

Example: Use of Multiple Cascading Style Sheets

Common.css

a img 
{
	border: none;
}

Public.css

@import url(“common.css”);

body 
{
	background-color: #CCC;
}

Vendors.css

@import url(“common.css”);

body 
{
	background-color: #AAA;
}

Administration.css

@import url(“common.css”);

body 
{
	background-color: #FFF;
}

This allows us to gain the true benefits of “cascading” style sheets, as each style sheet is importing and thus inheriting (or overloading, when appropriate) the parent style sheet(s).

Use discretion as to the number of CSS files to import against. In most cases, there is only need for a single import per CSS file. Several imports can become unwieldy and difficult to track down CSS class inheritance.

CSS Formatting

When adding CSS styles to a CSS style sheet, format the document in the following general sections:

Import CSS file(s)
HTML overloads
Custom CSS styles

Example: External CSS Sample

Example.css
import url(“common.css”);

body 
{
	background-color: #FFFFCC;
}

input, textarea, select 
{
	background-color: #FFFFAA;
}

/*Content found within the main content placeholder.*/
.content 
{
	padding: 15px;
}

/*Main navigation section.*/
.nav 
{
	border: 1px solid #000;
}
.nav ul 
{
	list-style-type: none;
}
.nav ul li 
{
	margin-left: 10px;
margin-right: 10px;
}

All HTML overloads are located first and grouped together, and custom styles make up the rest of the document. Notice, that all custom styles have comments as to what they affect to assist in maintaining changes. HTML overloads can have comments as well, but are not really necessary. Additionally, styles either as HTML overloads, or custom, that are cascading within a document should be grouped together without vertical spacing as demonstrated with “.nav”. Please note that there are no CSS styles applied to ID elements! These are commonly used as follows:

Example: CSS Style Based on Element ID

#nav 
{
padding: 15px;
}

The reason for this is that we want only class declarations in the HTML because it specifically tells the HTML observer (developer) that this object/tag has a CSS style applied to it, whereas the use of CSS against an ID tag does not convey this. When CSS is complex and uses repositioning or floats, etc. this can become very difficult to identify where HTML issues are occurring verses applied CSS. ID tags in HTML should be used for identification of objects/tags (for use with javascript/codebehind, etc.) and not for CSS styling.

Miscellaneous Formatting

Use spaces to enhance readability such as the following:

Bad:
.nav{background-color:#000000;}
.header{
	padding:10px;
}

Good:
.nav 
{
	background-color: #000000;
}

.header 
{
	padding: 10px;
}

Always terminate style attributes with a semi-colon, even when there is only one style attribute being applied.

Explicitly write out style attributes for less-common or hard to read attribute values instead of combining them into a single line:

Example(s): Readability

Bad:
padding: 10px 5px 10px 5px;

Good:
padding-top: 10px;
padding-right: 5px;
padding-bottom: 10px;
padding-left: 5px;

Additionally, when managing page layout widths, adjust widths from the highest containing item possible rather than specific nested objects. See the following HTML/CSS example:

…
<body>
	<div class="container">
		<div class="header">This is the header!</div>
		<div class="nav">
			<ul>
				<li>Link 1</li>
				<li>Link 2</li>
			</ul>
		</div>
		<div class="content">
			<p>Here is some content.</p>
		</div>
	</div>
</body>
…
CSS:

Bad:
.header 
{
	width: 780px;
}

.nav 
{
	width: 780px;
}

.content 
{
	width: 780px;
}

Good:
.container 
{
	width: 780px;
}

CSS Style Naming

Some common things to remember while naming styles:

  • Avoid using names which are too specific to the styles being applied, for example: “bluetext” or “leftNav”. Today you may be developing the text to be blue, or the navigation to be on the left, but tomorrow the text may change to green and the navigation may be right or top of the page.
  • Use consistent naming convention: Florida Family will be using camel case (“mainNav” or “nav”).
  • Don’t use acronyms or uncommon abbreviations such as “hlsm” (highlighted sub-menu).
  • Attempt to keep names fairly short, generally less than 15 characters.
  • .

  • Do not prefix CSS styles with “css” such as “cssNav”.

CSS Validation

Don’t forget, it’s never a bad idea to validate your CSS (W3 CSS Validator)!

Thanks for reading, I hope this makes life a little easier when working with your HTML/CSS projects!

Web Session Management and Centralization Class

For most web-based applications, developers employ some type of session-based data store/access. It’s fairly common practice to use session to pass data back and forth for the duration of a user’s visit, especially when that data is not completely persistent, as in the case of storing data directly back to the database.

Because this is such common practice, when reviewing code, I often find that developers simply access session whenever they need it from whichever page they are calling/saving from. Though this is probably the easiest way to do things it can get pretty messy.

Check out the following example of what I’m talking about:

A Messy, But Common Example of Session Usage

login.aspx.cs

protected void btnSubmit_Click(object sender, ImageClickEventArgs e)
{
    bool result = SomeBusinessEntity.CheckLogin(tbEmail.Text, tbPassword.Text);
    if (result)
    {
        //Add some values to session to be used later in the web-application
        Session.Add(“SomeSessionData”, “SomeSessionValue”);

        //do the rest of code you may want to call...
    }
    else
    {
        //provide notification of failed login
    }
}

memberonly.aspx.cs

protected void Page_Load(object sender, EventArgs e)
{
    //Check the session value to customize this page (or maybe, see if user is allowed access)
    string sessionValue = string.Empty;

    try 
    {
        if (string.IsNullOrEmpty(Session["SomeSessionValue"].ToString()))
        {
            //kick user out of page
        }
        else
        {
            //customize page with logic pertaining to the value stored in session
        }
    }
    catch
    {
        //kick user out of page
    }
}

There are several potential issues with writing code like this. First, code is scattered throughout the web application and it is inheritently decentralized making maintenance and debugging potentially difficult. Second, session values are stored in magic strings, meaning, that if another developer goes to use this session value, they need to know what string they need to request and if by chance they mistype it, they’re in trouble.

Note: There are additional issues with the above example, but for the purpose of this demo I’m going to focus just on these specific issues.

Implementing a Web Session Manager Class

Here’s the same code using a Web Session Manager class that we will create at the web application level.

protected void btnSubmit_Click(object sender, ImageClickEventArgs e)
{
    bool result = SomeBusinessEntity.CheckLogin(tbEmail.Text, tbPassword.Text);
    if (result)
    {
        //Add some values to session to be used later in the web-application
        //NOTICE THE STRONGLY TYPED REFERENCE
        WebSession.SomeSessionData = “SomeSessionValue”;

        //do the rest of code you may want to call...
    }
    else
    {
        //provide notification of failed login
    }
}

memberonly.aspx.cs

protected void Page_Load(object sender, EventArgs e)
{
    //Check the session value to customize this page (or maybe, see if user is allowed access)
    //NOTICE THE USE OF DATATYPES HERE AND ENCAPSULATING LOGIC TO ASSESS WHETHER THE SESSION VALUE EXISTS
    string sessionValue = WebSession.SomeSessionData;

    if (string.IsNullOrEmpty(sessionValue))
    {
        //kick user out of page
    }
    else
    {
        //customize page with logic pertaining to the value stored in session
    }
}

WebSession.cs

/// <summary>
/// Access SomeSessionData which describes what a logged in user's favorite color is.
/// </summary>
public static string SomeSessionData
{
    get
    {
        HttpContext context = HttpContext.Current;
        string sessionValue = string.Empty;

        if (context.Session["SomeSessionData"] != null)
        {
            sessionValue = context.Session["SomeSessionData"].ToString();
        }

        return sessionValue;
    }
    set
    {
        HttpContext context = HttpContext.Current;
        context.Session.Add("SomeSessionData", value);
    }
}

It’s different but not too drastically; so what did we gain from this?

Well, we’ve added a WebSession class which contains static methods that now house ALL session based information in a single area. We can readily access session values through static calls, make updates to session based content or logic through a single-point of entry, provide strongly typed access to that session information to help ourselves and our fellow developers, and finally, even provide intellisense hints through sandcastle notation (the triple “/” comments at the top of SomeSessionData).

Remember, this is a small demo example; imagine this on a much grander scale where you have possibly hundreds of pages each with their own implementations of directly calling the Session collection! Not good my friends.

Hope this short article on developing and implementing a Web Session Manager class helps!

P.S. – Go Gators! 2008-2009 National Football Champions!

University of Florida