This is my third and last post in the series of writing multi-tenant applications. Before this I have discussed how to avoid if/else type structures and how context based dependency injection can help us do it efficiently. In this post I will focus on the UI side of the application.
One of the main traits of the MVC framework is its various extension points it has on offer. It empowers the developer to extract the aspects from the core logic. The aspect I am taking in this series of posts is a shared code-base for multiple regions or clients. There are few approaches you can take, depending on how complex your requirements are:
1) Virtual Path Provider: This enables you to control the physical location of your views. Hence you can choose which view to render when required. You can construct a different physical path based on the relative path for current request.
e.g. For index.cshtml
- /views/contact/au/index.cshtml
- /views/contact/uk/index.cshtml.
2) Custom View Engine: This is a bit more powerful way to control where your views reside. You can go a step further and build your views dynamically. There is a good article on how a Custom View Engine can be used to render dynamic views from the database: http://www.dotnetcurry.com/showarticle.aspx?ID=946
3) Default Display Mode : Display mode is mainly used to interpret request headers to target different client devices such as iPhone, android or desktop etc. There is a good example where it is used to choose a different view based on a setting in session.
http://ericsowell.com/blog/2011/9/27/doing-crazy-things-with-asp-net-mvc-4s-display-modes
Similarly we can have different view to target the tenants of our application such as:
- view.au.cshtml
- view.uk.cshtml
- view.client1.cshtml
This approach can result in very complicated view mapping if we have views targeting different aspects including targeting client device.
4) ActionNameSelectorAttribute: Using this we can control not only which view to render but also what Action to process the request. By inheriting ActionNameSelectorAttribute we get a method called IsValidName, where we can assess current context, rout values etc to find out if this is the Action that should serve the current request.
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) { if (!Name.Equals(actionName, StringComparison.OrdinalIgnoreCase)) return false; this.ContextProvider = GetContextProvider(controllerContext); //here we need some mechanism to current context provider. var value = this.ContextProvider.GetType().GetProperty(this.ContextKey).GetValue(this.ContextProvider, null); return value != null && ((string)value == this.ExpectedValue); }
Usually there is a small variation in the action code as well along with the view changes, so I prefer this approach. It also gives an opportunity to target different actions based on the query string/route values.
Along with my previous posts I see the entire picture to write a multi-tenant web app as shown in this image below:

[…] we get to implement very complex logic and there are various ways to manage the complexity. In my 3 Posts series, I have discussed how we can handle complexity introduced by the multi-tenancy aspect of the […]
LikeLike