I'm looking at implementing a custom RazorViewEngine
. Basically I have two sites with effectively the same code base. The differences being that they look different. I want to override the standard view engine to make MVC look in two separate locations for it's views, layouts, etc. one for company A and another for Company B. Company A will contain the master views and company B's view will override these masters. So I want the View Engine to look in location B for a view, layout, master or partial if it finds it then return it, if it doesn't find it I want it to default to company A's views as the default. Obviously company A will only look in it's own folder.
Ok to the crux of the question:
I've found this site: http://www.aspnetwiki.com/mvc-3-razor:extending-the-view-engine
First question, is this the best way to achieve this?
Second do I need to override the CreatePartial
, CreateView
, FindPartial
and FindView
methods?
Update
Ok I've figured out the second question myself, the Methods I want to override are CreateView
and CreatePartialView
as at this point it's built the view string and I can fiddle with it.
Ok in the end I opted for an approach detailed here: http://weblogs.asp.net/imranbaloch/archive/2011/06/27/view-engine-with-dynamic-view-location.aspx
thanks to @Adriano for the answers and pointers but in the end I think this approach fits my needs better. The approach below allows me to keep the standard functionality but to create a new higher priority view location to be searched.
public class Travel2ViewEngine : RazorViewEngine
{
protected BrandNameEnum BrandName;
private string[] _newAreaViewLocations = new string[] {
"~/Areas/{2}/%1Views/{1}/{0}.cshtml",
"~/Areas/{2}/%1Views/{1}/{0}.vbhtml",
"~/Areas/{2}/%1Views//Shared/{0}.cshtml",
"~/Areas/{2}/%1Views//Shared/{0}.vbhtml"
};
private string[] _newAreaMasterLocations = new string[] {
"~/Areas/{2}/%1Views/{1}/{0}.cshtml",
"~/Areas/{2}/%1Views/{1}/{0}.vbhtml",
"~/Areas/{2}/%1Views/Shared/{0}.cshtml",
"~/Areas/{2}/%1Views/Shared/{0}.vbhtml"
};
private string[] _newAreaPartialViewLocations = new string[] {
"~/Areas/{2}/%1Views/{1}/{0}.cshtml",
"~/Areas/{2}/%1Views/{1}/{0}.vbhtml",
"~/Areas/{2}/%1Views/Shared/{0}.cshtml",
"~/Areas/{2}/%1Views/Shared/{0}.vbhtml"
};
private string[] _newViewLocations = new string[] {
"~/%1Views/{1}/{0}.cshtml",
"~/%1Views/{1}/{0}.vbhtml",
"~/%1Views/Shared/{0}.cshtml",
"~/%1Views/Shared/{0}.vbhtml"
};
private string[] _newMasterLocations = new string[] {
"~/%1Views/{1}/{0}.cshtml",
"~/%1Views/{1}/{0}.vbhtml",
"~/%1Views/Shared/{0}.cshtml",
"~/%1Views/Shared/{0}.vbhtml"
};
private string[] _newPartialViewLocations = new string[] {
"~/%1Views/{1}/{0}.cshtml",
"~/%1Views/{1}/{0}.vbhtml",
"~/%1Views/Shared/{0}.cshtml",
"~/%1Views/Shared/{0}.vbhtml"
};
public Travel2ViewEngine()
: base()
{
Enum.TryParse<BrandNameEnum>(Travel2.WebUI.Properties.Settings.Default.BrandName, out BrandName);
AreaViewLocationFormats = AppendLocationFormats(_newAreaViewLocations, AreaViewLocationFormats);
AreaMasterLocationFormats = AppendLocationFormats(_newAreaMasterLocations, AreaMasterLocationFormats);
AreaPartialViewLocationFormats = AppendLocationFormats(_newAreaPartialViewLocations, AreaPartialViewLocationFormats);
ViewLocationFormats = AppendLocationFormats(_newViewLocations, ViewLocationFormats);
MasterLocationFormats = AppendLocationFormats(_newMasterLocations, MasterLocationFormats);
PartialViewLocationFormats = AppendLocationFormats(_newPartialViewLocations, PartialViewLocationFormats);
}
private string[] AppendLocationFormats(string[] newLocations, string[] defaultLocations)
{
List<string> viewLocations = new List<string>();
viewLocations.AddRange(newLocations);
viewLocations.AddRange(defaultLocations);
return viewLocations.ToArray();
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
return base.CreateView(controllerContext, viewPath.Replace("%1", BrandName.ToString()), masterPath);
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
return base.CreatePartialView(controllerContext, partialPath.Replace("%1", BrandName.ToString()));
}
protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
{
return base.FileExists(controllerContext, virtualPath.Replace("%1", BrandName.ToString()));
}
}
then register in Gloabal.asax
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
//Register our customer view engine to control T2 and TBag views and over ridding
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new Travel2ViewEngine());
}
Yes that's the way but you do not need to override that methods. RazorViewEngine
inherits from VirtualPathProviderViewEngine so you can use its properties to set the location of your views.
For an example read Creating your first MVC ViewEngine and How to set a Default Route (To an Area) in MVC.
Here's my answer:
Add this to your global.ascx
ViewEngines.Engines.Clear();
var customEngine = new RazorViewEngine();
customEngine.PartialViewLocationFormats = new string[]
{
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Partial/{0}.cshtml",
"~/Views/Partial/{1}/{0}.cshtml"
};
customEngine.ViewLocationFormats = new string[]
{
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Controller/{1}/{0}.cshtml"
};
customEngine.MasterLocationFormats = new string[]
{
"~/Views/Shared/{0}.cshtml",
"~/Views/Layout/{0}.cshtml"
};
ViewEngines.Engines.Add(customEngine);
those are the folders where razor checks your views.
Let me know if this works.