24

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.

2
  • Do you want to keep two different web-sites in the same folder? Commented Mar 23, 2012 at 11:58
  • What I'm saying is I want the views to be in separate folders but for them to share the same controller/model. Commented Mar 23, 2012 at 12:30

3 Answers 3

19

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()); } 
Sign up to request clarification or add additional context in comments.

2 Comments

you could use _newMasterLocations.Union(MasterLocationFormats).ToArra() instead of creating custom method
link does not work, here is working webarchive web.archive.org/web/20120328005525/http://weblogs.asp.net/…
7

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.

2 Comments

This seems interesting but I'm not convinced it does exactly what I want it to do, documentation is a bit sparse, do you have an example you can post?
It's just a small example but: coderjournal.com/2009/05/creating-your-first-mvc-viewengine and this very nice link here on SO: stackoverflow.com/questions/2140208/…
4

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.

3 Comments

I have implemented the same thing but somehow viewstart.cshtml is not rendering properly
what do you mean by not rendering properly? what errors are you getting?
the layout was not set properly. I had identified the issue. I had kept my views outside of the views folder and by default, viewstart cshtml would be searched from current view path and hierarchically towards the root until its found. So in my case, I have kept the viewstart in root directory and its working now.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.