# “Pluggable” routes in ASP.NET MVC

How do I handle multi-tenancy with routes in ASP.NET MVC?

The application is a multitenant ASP.NET MVC 3 application. Tenants are identified based on hostname so this does not interfere with routing.

Other important information:

• The application has a number of features (or plugins). These are essentially other web projects with their own controllers, views and routes.

• ASP.NET MVC routing serves as our entry point into these features.

• One key point is that all the features function the SAME way for all tenants. The variable is whether the tenant is actually subscribed to the feature.

So imagine we have a "blog" feature with a route like so:

/blog/{instance}/posts


When our application starts we register the routes of all "routable" features. However, we need to ensure that this route is only matched if the tenant is subscribed to that feature.

Currently I'm doing this with a custom route:

private readonly IFeatureMetadata feature;

public FeatureRoute(IFeatureMetadata feature, string url, RouteValueDictionary defaults, IRouteHandler handler) :
base(url, defaults, handler)
{
if (feature == null)
throw new ArgumentNullException("feature");
this.feature = feature;
}

public override RouteData GetRouteData(System.Web.HttpContextBase httpContext)
{
var routeData = base.GetRouteData(httpContext);

if (routeData == null)
return null;

var context = DependencyResolver.Current.GetService<ISiteContext>();
var site = context.GetSite();
var matchedInstances = site.FeatureInstances.Where(f => f.Feature.FeatureType == feature.FeatureType);

if (!matchedInstances.Any())
return null;

if (routeData.Values["instance"] == null)
{
routeData.Values["instance"] = matchedInstances.First().Id;
}

return routeData;
}


One thing that I need to cater for is routes that do not specify an instance.

For example, a tenant may have one or more blogs. If he only has one then outgoing routes should just generate:

http://[domain]/blog/posts


If he has more than one then we should generate:

http://[domain]/blog/{instance}/posts


Currently I'm handling this by registering 2 routes (one with the {instance} parameter and one without). In the code above you can see that if the instance parameter doesn't exist in RouteData we just add the id of the first matched feature instance.

This is then sufficient for me to make a call to my blog service:

GetPosts(int featureInstanceId);


While everything "works", I'm looking for feedback on my implementation. As a freelance developer this seems to be the only way I can get my code reviewed.