If I understood correctly, users could have simultaneously open 2 different tabs, each with a different tenant. And each page should display data relevant to each tenant.
So that means a solution involving a cookie or the session needs to be discarded as the tenant is specific to each browser tab.
And reading your answer to Cyril Gupta´s suggestion, I understand the hidden tenantId on each page may not be submitted on every AJAX request.
Of course, one solution could be to modify your application and make sure this is always the case with every AJAX request.
Otherwise that would also discard a global filter based on the request parameters as the tenantId may not always be there.
I think the best option then is to add a segment in the URL holding the tenantId.
For example replacing the default route with something like the following route (If you have many different routes you would need to be very careful to avoid route collision):
routes.MapRoute(
name: "Default",
url: "{tenant}/{controller}/{action}/{id}",
defaults: new { tenant = "defaultTenant", controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This way you can make sure the tenant will always be submitted on each request, and you could also have 2 different tabs with different tenants displaying each the appropriate data.
There are different options on how to recover the value of the route segment.
The binding will automatically populate the value on any parameter named "tenant" on your action method, or any parameter named "tenant" in a model class that is a parameter of the action method:
public ActionResult Foo(FooModel model, string tenant)
{
//both tenant and model.tenant will contain the value of the URL segment
return View();
}
You could also write a filter that access the value of the route parameter (RouteData is a property of the ActionExecutingContext
and ActionExecutedContext
class received as parameters of the filter methods), and performs some logic. The filter would then be set as a global filter in your application or to your base controller:
public class FooFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var tenant = filterContext.RouteData.Values["tenant"]
//do whatever you need to do before executing the action, based on the tenant
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var tenant = filterContext.RouteData.Values["tenant"]
//do whatever you need to do after executing the action, based on the tenant
}
}
The final option is to directly access the RouteData parameter on your base controller class. (As RouteData is a property of the base MVC Controller
class)
As long as you are using Html and Ajax helpers for URL generation, the tenant segment of the URL will be maintained in your links. However if you have jquery code directly sending AJAX calls with hardcoded URLs, you would then need to update that code so the new url segment is taken into account.
Finally, in case the tenantId values are something not very user friendly like an integer, you could have unique names for each tenant and use the names in the URL. You would then add some logic that maps it to the integer value your application need.