I have implemented a multi-tenant view engine similar to what is described here:
Which let me override the search locations for view like this:
MasterLocationFormats = new[]
{
"~/Views/%1/{1}/{0}.cshtml",
"~/Views/%1/Shared/{0}.cshtml",
"~/Views/Default/{1}/{0}.cshtml",
"~/Views/Default/Shared/{0}.cshtml",
};
In which the %1
is replaced with the correct folder for the active tenant. This is working just fine exception one problem. When I define the Layout path on my view like this:
Layout = "~/Views/Default/Shared/_MyLyout.cshtml";
It kind of defeats the purpose of having the multi-tenancy since I have have to hard code the exact location of the layout page. I want to be able to do something like this:
Layout = "~/Views/%1/Shared/_MyLyout.cshtml";
If I wanted to allow tenants to have their one layout pages, how would I go about supporting this?
I have tried fiddling with the view engine methods that I overrode:
- CreatePartialView
- CreateView
- FileExists
But nothing seems to point itself towards being able to dynamically specify the layout page.
Update:
Here's what I have working so far. I used the answer to this question https://mcmap.net/q/1012120/-how-to-set-razor-layout-file-just-specifying-the-name slightly modified to create a HTML helper:
public static string GetLayoutPageForTenant( this HtmlHelper html, string LayoutPageName )
{
var layoutLocationFormats = new[]
{
"~/Views/{2}/{1}/{0}.cshtml",
"~/Views/{2}/Shared/{0}.cshtml",
"~/Views/Default/{1}/{0}.cshtml",
"~/Views/Default/Shared/{0}.cshtml",
};
var controller = html.ViewContext.Controller as MultiTenantController;
if( controller != null )
{
var tenantName = controller.GetTenantSchema();
var controllerName = html.ViewContext.RouteData.Values["Controller"].ToString();
foreach( var item in layoutLocationFormats )
{
var resolveLayoutUrl = string.Format( item, LayoutPageName, controllerName, tenantName );
var fullLayoutPath = HostingEnvironment.IsHosted ? HostingEnvironment.MapPath( resolveLayoutUrl ) : System.IO.Path.GetFullPath( resolveLayoutUrl );
if( File.Exists( fullLayoutPath ) ) return resolveLayoutUrl;
}
}
throw new Exception( "Page not found." );
}
which is similar to what saravanan suggested. Then I can set the layout in my view with this code:
Layout = Html.GetLayoutPageForTenant( "_Home" );
Unfortunately, this duplicates the work that the custom view engine is doing which seems like the wrong way to go.