Introduction
Many small companies having limited budget for website hosting prefer to host multiple websites in a single shared
hosting space using domain pointers. The shared hosting plans offered by many web hosts use some third party URL
rewriting software or IIS URL rewriting module to segregate one website from the other. Often these techniques require
that you keep files of individual website into a folder of its own. This approach goes well with websites using totally
different codebase. However, if the websites under consideration are using the same codebase this approach may not be
suitable. In such cases you need to take control in your own hands by programmatically handling multiple domains and
sub domains. This article illustrates one possible approach to do just that.
Defining the problem
Consider that you have two domain names with you, say domain1.com and domain2.com. Now, you wish to host these two
domains in a single hosting space. As mentioned earlier if you use some software for URL rewriting, you will be
expected to segregate multiple websites into a folder of their own. Have a look at Figure 1.
Figure 1: Sample folder structure of a website

The folder
structure as shown in Figure 1 belongs to two websites -domain1.com and domain2.com. You will be repeating similar
folder structure for all the websites you wish to host under the same hosting account. The "separate folder for each
website" approach works well when the websites under consideration are totally independent of each other and there are
very few things common between them. In such cases, they use different codebase (in terms of web forms, user controls,
classes, components and such items) and database tables.
Now, consider a case that you wish to build two websites
using a common codebase or framework. In this case, the web forms, user controls, classes and components will be the
same for all the websites built using the same codebase. What changes is the content or data of the websites and
possibly the look and feel of the websites. Suppose that you wish to develop two shopping websites -one selling
electronic gadgets and the other selling books. Obviously both of the websites will have many features common and you
can develop a framework that can be used by both of them. These websites will look different and work with their own
data but at code level they share the same functionality (membership, product catalog, shopping cart etc.). In such
cases repeating the same codebase in many folders is not a good idea. Tomorrow if the codebase changes you will need to
synchronize all the folders with the new codebase. Some other examples where the same code base can be used for
multiple websites are Content Management Systems (CMS), portals, blog engines, community websites and discussion
forums.
There is one more point that needs some consideration while using "separate folder for each website"
approach. Not all web hosts use the same software for URL rewriting. When you shift from one web host to another, this
can pose a problem because you may need to adjust your website structure or configuration to meet requirements of the
software used by your new web host.
To conclude, there are times when you need to take control of dealing with
multiple websites in your own hands by programmatically handling multiple domains and sub domains. In the remainder of
this article you will learn one approach to do the same.
The Solution
As a solution to the problem at hand, you will develop a database driven scheme that maintains "metadata" of
multiple websites. The metadata includes website ID, title, host, Master Page, default page and so on. Notice that here
"host" refers to www.domain1.com, domain2.com etc. In all the tables that store the website data such as menus, users,
pages and other application specific data, you will have a column that stores website ID. Thus all the data belonging
to a website can be retrieved using the ID of that website. The metadata to be stored will be clear when you create
required database tables in the later section.
Once your website metadata is in place, you are ready to handle
incoming requests. You will essentially check the incoming request URL to determine the host (domain1.com or
domain2.com) and then fetch website metadata for that host. You then apply master page, theme and other settings on the
fly. From the end user's perspective, they will see two different websites, each with its own content, layout and look
and feel.
For our solution to work, you must ask your web hosting provider to point additional domain pointers to
your primary domain. For example, if you have hosted domain1.com with your web host and also wish to run domain2.com
from the same hosting space, you will need to ask them to point domain2.com to the same space as domain1.com. If you
wish to host multiple sub-domains -e.g. blog.domain1.com, photos.domain1.com -again you will need to get in touch with
your hosts and ask them to do the required configuration. Note that some web hosting companies allow domain pointers
free of charge whereas some charge a small fee. Some allow you to do it on your own via their web based control panel
whereas some expect you to seek help from their support personnel. You need to figure out what their offerings are and
add the required domain pointers accordingly.
Figure 2: Multiple root domain pointers

Now, let's begin developing a
sample that illustrates what we discussed so far.
Creating SQL Server database
You will use a SQL Server database to store website metadata. So, begin by creating a new ASP.NET website and add
App_Data folder to it. Then add a new SQL Server Express database to App_Data folder and create two tables - Websites
and Pages -as shown in Figure 3.
Figure 3: Tables storing website and page information

As you can see the WebSites table consists of six columns viz. WebSiteId, Title, Host, Theme, MasterPage and
DefaultPage. WebSiteId column represents the primary key and is a GUID. Title column is a descriptive name for the
website and you can also use it for page title if you so wish.
The Theme, MasterPage and DefaultPage columns
indicate theme name, master page and default page of the website respectively. MasterPage and DefaultPage values are
URLs to .master and .ascx files (e.g. ~/MasterPages/Master1.master, ~/UserControls/Default1.ascx) respectively. The
DefaultPage column needs a bit of explanation. As a part of the framework you will have just one default page
(Default.aspx) but at runtime based on the target website you will need to render different content in it. You achieve
this by putting the markup and code, that otherwise would have gone in a default page, into a user control. Based on
the MasterPage URL and DefaultPage URL, the appropriate master page will be applied and the appropriate user control
will be loaded at runtime.
The Host column is important as it holds the domain or sub-domain name (e.g.
www.domain1.com, domain1.com, blog.domain1.com). For every unique website host you wish to support, you need to have an
entry in the WebSites table.
Pages table stores web pages belonging to the websites and has four columns viz.
PageId, WebSiteId, Title and PageContent. PageId uniquely identifies a page and WebSiteId indicates the website to
which the web page belongs. This way you can restrict pages only to a particular website. Title is a friendly name for
a page and PageContent holds the actual HTML markup that makes the page. In a more real world website you will have
additional columns to Pages table for tracking user activity and statistics. You will also have many more tables that
store data specific to the framework you are building and each table will have WebSiteId column that associates its
data to a particular website. For our purpose it is suffice to have Pages table.
Creating Entity Framework Models
In order to access data in the WebSites and Pages tables, you will create Entity Framework Data Model. Add a new
ADO.NET Entity Data Model in App_Code folder (Figure 4).
Figure 4: Adding a new ADO.NET Entity Data Model

Then drag and
drop WebSites and Pages tables from Server Explorer onto the Entity Data Model designer so as to create models as shown
in Figure 5.
Figure 5: Entity Data Models for Website and Pages tables

Creating a helper class
Next, you will create a helper class -WebSiteHelper -that exposes four important methods. You will be using these
methods throughout the website in various web forms. The methods of WebSiteHelper class are listed below:
GetCurrentWebSite()
GetTheme()
GetMasterPage()
GetDefaultPage()
The GetCurrentWebSite() method returns an instance of WebSite model class that represents
metadata of the current website. The GetTheme(), GetMasterPage() and GetDefaultPage()
methods return theme name, master page URL and default page URL respectively. Skeleton of WebSiteHelper class
is shown below:
As you can see all the methods of WebSiteHelper class are static so that they can be called without
instantiating it. These methods are discussed in detail in the next sections.
Detecting and fetching metadata for current website
The GetCurrentWebSite() method is pivotal to the functioning of our solution since it fetches metadata
from the WebSites table by inspecting host from the current URL. The complete code of GetCurrentWebSite()
method is given below:
As you can see, the method begins by declaring a dictionary for storing WebSite model instances. When you
will be testing our solution on a local machine, you will not be accessing a website with domain as such (your URL will
be http://localhost/... and not http://www.domain1.com/...). To simplify the testing
process, you will have an <appSettings> section with a key -WebSiteId. If this key has some value,
you return metadata for that website without inspecting the host. The following fragment shows how the
<appSettings> section looks like:
If WebSiteId key is kept to an empty string, you fetch all the WebSite model instances and
store them in websites dictionary. The host acts as a key and WebSite model instance as the value. For performance
reasons, you put the dictionary in ASP.NET Cache object. Notice that the cache duration is 1 day and you
should change it as per your requirements.
Next, the code examines the host for the current URL. This is done
using Request.Url property and then examining the Host property. The Request.Url property
returns an instance of System.Uri class and represents a uniform resource identifier (URI). It also allows an easy
access to the parts of the URI such as Host. Depending on the host, a WebSite instance is returned from the cached
dictionary.
If the <appSettings> section mentions some WebSiteId, a WebSite model instance is
directly fetched from the database and returned to the caller.
Deciding Theme, Master Page and Default Page at runtime
The remaining three methods -GetTheme(), GetMasterPage() and GetDefaultPage()
-make use of GetCurrentWebSite() method and return theme name, master page URL and default page
URL respectively. These methods are shown next:
Creating Theme, Master Pages and Default Pages for testing
Now that you have WebSiteHelper class ready, let's create sample themes, master pages and default pages for testing
purpose. Add two themes to your website and name them as Blue and Brown. Also add a couple of master pages and user
controls. After adding all these items your website folder structure should resemble Figure 6.
Figure 6: Adding sample themes, master pages and default pages

We won't discuss themes and master pages in detail as they are quite simple in nature. You can
download the code accompanying this article and copy them into your website for testing purpose.
Both the user
controls representing default pages contain a GridView that lists all the pages belonging to the current website
(Recollect that Pages table has WebSiteId column that indicates the website to which the page belongs). The following
fragment shows the GridView markup:
The HyperLinkField points to ShowPage.aspx and passes PageId to it. This way ShowPage.aspx
can display contents of the requested page. The code behind of the user control binds this GridView with
data.
As you can see, the above code fetches data from Pages table by filtering it based on WebSiteId and binds
it with the GridView.
What is more important is the default page (.aspx) of the website. This page shows how
themes, master pages and default pages are applied on the fly from the database. Add a web form -Default.aspx -to the
root folder of the website and add a PlaceHolder control on it. You will need to handle two events of the web form -
Page_PreInit and Page_Load. You must set Theme and Master Page of the web forms in
Page_PreInit event handler. Once the page is loaded you can load user control acting as the default page.
The following code shows how this is done.
As you can see, Page_PreInit event handler does the job of setting Theme and MasterPageFile
properties of the web form. It uses methods of WebSiteHelper class to retrieve theme and master page for the current
host.
The Page_Load event handler retrieves the URL of default page user control and then loads it
using LoadControl() method. The dynamically loaded user control is then added to the Controls collection
of the PlaceHolder control. Thus Default.aspx will load a user control depending on the host and display different
content when the default page is accessed.
Displaying website pages
ShowPage.aspx does the job of displaying a particular page. To create ShowPage.aspx, add Pages folder to the website
and add ShowPage.aspx web form to it. Place a Literal control on the web form. This Literal control is used to render
the page contents.
In the Page_PreInit event handler of ShowPage.aspx, set Theme and MasterPage as
you did for Default.aspx.
In the Page_Load event handler, add the following code:
As you can see, the code fetches a single Page instance based on a PageId. The "if"
condition checks whether the requested page belongs to current website. If so, its PageContent markup is assigned to
the Literal control, otherwise an error message is displayed.
Testing our solution
In order to test our solution, you need to ensure that required domain pointers are properly set. You also need to
add some test data in the WebSites and Pages table.
Add at least two records in the WebSites table as shown
below:
Table 1: Configuration
Item | Website 1 | Website 2 |
Title | This is WebSite 1 | This is WebSite 2 |
Host | www.domain1.com | www.domain2.com |
Theme | Brown | Blue |
MasterPage | ~/MasterPages/MasterPage1.master | ~/MasterPages/MasterPage2.master
|
DefaultPage | ~/DefaultPages/DefaultPage1.ascx | ~/DefaultPages/DefaultPage1.ascx <
/td>
|
Make sure to change host column values to reflect your domain names.
Now run Default.aspx using
domain1 as host (i.e. www.domain1.com/default.aspx) and you should see default page as shown in Figure 7.
Figure 7: Sample run with domain1.com

Now access Default.aspx using
domain2 as host (i.e. www.domain2.com/default.aspx). This time you should see default page as shown in Figure
8.
Figure 8: Sample run with domain2.com

See how the same web form changes
its display and contents depending on the host. Similarly, test the functionality of ShowPage.aspx by clicking on the
respective page links from the GridView.
Summary
At times you need to run multiple websites from the same hosting space. If the websites are using the same codebase,
you can device a database driven approach to handle multiple domains and sub-domains. This article illustrates one such
approach. The approach discussed in this article doesn't need any external URL rewriting technique. Once you develop
such a database driven framework you can support as many websites as you want by pointing additional root domain
pointers. In addition to being cost effective it also simplifies future upgrades to the framework. Since all the
websites are using the same base framework, upgrading the framework will affect all the websites. You can extend the
approach discussed in the article while developing content management systems (CMS), portals, blog engines, shopping
websites, discussion boards or any such types of framework supporting multiple websites.
About Bipin Joshi
 |
Bipin Joshi is a blogger, author and a Kundalini Yogi who writes about apparently unrelated topics - Yoga & Technology! A former Software Consultant and trainer by profession, Bipin is programming since 1995 and is working with .NET framework ever since its inception. He is an internation...
This author has published 7 articles on DotNetSlackers. View other articles or the complete profile here.
|
You might also be interested in the following related blog posts
Refactoring the GoF patterns (Singleton in .NET continued
read more
Singleton in .NET
read more
Managing Windows Workflow Events on a Web Server
read more
Bounded blocking queues
read more
|
|
Please login to rate or to leave a comment.