As I was setting up a demo project on my local computer for dealing with a Multi-Tenancy application, I came across with some problems (like many other times). The issue this time had to do with accessing the same web application using different host names, each one employed for handling a different tenant (client).
So, here are the first steps to configure Hosts file in Windows, Visual Studio Web Applications and IIS and to begin to work properly with this kind of multi-tenant functionality.
To begin with, it is necessary to update the "host" file located at "C:\Windows\System32\drivers\etc". This file contains the mappings of IP addresses to host names. So, you have to add the host names you want to use to access your Local or Express IIS Server. As you can see below, I have added three different host names, "tenant1", "tenant2" and "tenant3", mapped to 127.0.0.1 IP address, which is assigned by default to local machine.
There are also three additional lines, mapped to "::1" address. This is for dealing with IPv6 IP addresses. Previous lines manage IPv4 IP addresses. You can obtain more information at here.
VISUAL STUDIO WEB APPLICATION PROJECT
I have set up my Visual Studio Web Application Project to make use of Local IIS, using "http://localhost/MultitenancyDemo" as a project URL . The application will be accessed from "http://tenant1/MultitenancyDemo", "http://tenant2/MultitenancyDemo" and "http://tenant3/MultitenancyDemo" URLs, passing the responsibility of serving different content to the same web application (based on host name).
Once you have filled up the "Project Url" field, you should "Create a Virtual Directory" in the Local IIS.
LOCAL IIS (INTERNET INFORMATION SERVER)
Just after creating the new virtual directory, this will appear under the Default site (in most cases) or the mapped site based on "Project Url" you entered before. Without any other type of specification the virtual directory will be added to the website using "port=80" and "protocol=http" in its binding properties.
Here is the Default site along with the new just created application:
And below is the basic configuration from the virtual directory:
At last, here is the basic configuration for the binding on "Default" site.
Eventually, you will be able to gain access to the same web application using different host names. The application will manage each different tenant to show different information, styles, images, etc:
I expect to write a little more about this topic, but it will be later....The main rule to accomplish is to extract the "host" name from the url and employ it to select, filter and secure any kind of returned information. For now, here it is the structure of my solution:
Briefly, I'm using a typical MVC Web application project with some little new features.
- I've incorporated a ViewModels folder to include a new layer of abstraction between de business model and the user interface (UI). It's not really necessary but it gives you more flexibility to deal with changes of functionality.
- "Content" folder is divided into different separated folders to contain "Texts", "Images", "Styles", etc. independently. It will be very useful in case you have to translate and localize the application to other languages.
- "Helpers" folder will contain classes to be used as "helpers" all over the application, as its own name implies.
- "DataAccess" will contain the employed classes to access data from database. As I indicate at "References" section, I've based this solution on that source code (Simple Multitenancy with ASP.Net MVC 4). Then, a LinqToSql class "DataClasses.dbml" is used to interact with a SQL Server Database (this database only holds a very simple "Tenant" table with an "Id" as primary key and simple nvarchar fields to save basic information) and a "LinqTenantDataProvider" class implementing "ITenantDataProvider" to interact with LinqToSql classes.
Below are some key pieces of my code:
- Global.asax file
public class MvcApplication : System.Web.HttpApplication
protected void Application_Start()
internal protected void Application_BeginRequest(object sender, EventArgs e)
// Get the client host name from the incoming request.
GlobalHelper.Host = this.Request.Headers["Host"].ToLower();
As it is said at www.w3.org, the Host request-header field specifies the Internet host and port number of the resource being requested. The Host field value must represent the naming authority of the origin server or gateway given by the original URL. This allows the origin server or gateway to differentiate between internally-ambiguous URLs, such as the root "/" URL of a server for multiple host names on a single IP address.
In other words, Host header lets you manage requests over differents domains (or subdomains) on the same IP Address.The Host header will be used by the web-server to bind incoming requests to the correct host or website. Then, you have the possibility of using your website to read that header manually and provide different behavior according to different domains (or subdomains) addressed. This is exactly what I'm doing in the code above whitin Application_BeginRequest handler.
- ITenantDataProvider interface:
public interface ITenantDataProvider
Tenant GetTenant(string host);
- LinqTenantDataProvider class:
public class LinqTenantDataProvider : ITenantDataProvider
public Tenant GetTenant(string host)
// Check host name.
// Throws an exception if host is not present
throw new ArgumentNullException("host");
// Removes port number if necessary
int portNumberIndex = host.LastIndexOf(':');
if (portNumberIndex > 0)
host = host.Substring(0, portNumberIndex);
// Get an instance of LINQ to SQL Data Context Class
DataClassesDataContext database = new DataClassesDataContext();
// Matches the host name with the content of host field name from stored information
Tenant result = (from tenant in database.Tenants
where tenant.Host == host
// Return the selected tenant or null if not encountered
- Home Controller:
public class HomeController : Controller
public ActionResult Index()
// Retrieve the client-specific data from the data provider.
ITenantDataProvider provider = new LinqTenantDataProvider();
Tenant tenant = provider.GetTenant(GlobalHelper.Host);
// Create model
TenantBaseModel model = new TenantBaseModel();
if (tenant != null)
// Data from different tenants
model.Name = tenant.Name;
model.Title = tenant.Title;
model.Theme = tenant.Theme;
model.About = tenant.About;
// Data from default site
model.Name = RGlobal.DefaultSiteName;
model.Title = RGlobal.DefaultSiteTitle;
model.Theme = string.Empty;
model.About = string.Empty;
// Return the Index view with the ViewModel data.
return View(new TenantBaseViewModel(model));
All of the above commented works fine by using Local IIS, but I haven't be able to make work properly using IIS Express. So, I'd really appreciate some kind of help for me....
http://www.dennisonpro.info | Simple Multitenancy with ASP.Net MVC 4