Friday, April 29, 2016

Search for view files in a custom location only for Specified Area in MVC 5

Leave a Comment

I'm looking to override the 'ViewEngine' for MVC5 in a way that first, it find my pages.. which already i failed.

Second, It only operate on a single Area {root}/{area}/{controller}/{action}/{etc.}

as so far as i googled it, i found several topic and answers, but they didn't fit my need. so i rather to ask it here, maybe i'm wrong with something...

public class CustomAreaViewEngine:RazorViewEngine {     public CustomAreaViewEngine()     {         var viewLocations = new[]         {             "~/App/pages/{1}/{0}.cshtml",             "~/App/pages/{1}/{0}.vbhtml"         };         AreaMasterLocationFormats = viewLocations;         AreaPartialViewLocationFormats = viewLocations;         AreaViewLocationFormats = viewLocations;     }      public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)     {          var viewEngineResult = base.FindPartialView(controllerContext, partialViewName, useCache);         return viewEngineResult;     }      public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)     {         controllerContext.RouteData.Values["controller"] = controllerContext.RouteData.Values["controller"].ToString().ToLower();          var viewEngineResult = base.FindView(controllerContext, viewName, masterName, useCache);         return viewEngineResult;     } } 

The method 'FindView' returns empty [first problem]

Global Config:

AreaRegistration.RegisterAllAreas(); ViewEngines.Engines.Add(new CustomAreaViewEngine()); // Look View Inside App/Pages/{1}/{0}.cshtml/.vbhtml 

My Hierarchy

Root --/App --/--/pages --/--/shared (but not that shared) --/... --/Area --/--/View --/--/--/Controller --/--/--/View(MVC BASED HIERARCHY, I Don't want to use in most case, and redirect to App) --/--/--/... --/--/... --/Controller --/View --/--/...(MVC BASED HIERARCHY) --/... 

EDITS:


EDIT1:


Changes i did due to @James Ellis-Jones answers:

Images: My Route Config: My Route Config Area Provided Route Config: Area Provided Route Config Global Config: Global Config My View Engine: My View Engine

still when i use http://localhost:1422/view/home/index i receive an error, which exist in my other home (View, related to the main controller, not the area controller.) it bring a wrong file.

Another Issue I Figured Out, Which Didn't Worked Too

My namespaces was wrong in last edit, and i changed them, but it didn't worked out too.

namespaces: new[] { "RavisHSB.Areas.View.Controllers" } 

EDIT2:


Changes i did due to @CarlosFernández answers:

I add ViewEngines.Engines.Clear(); it somehow went one step ahead. but still doesn't work.

New Global.aspx: My Global.aspx And i face this new error: The new Error

2 Answers

Answers 1

Try this: in RouteConfig.cs where you are setting up the route which links to a controller which you want to find the custom view location, add this:

var route = routes.MapRoute(<route params here>); route.DataTokens["area"] = "AreaName"; 

This will tell MVC that when you follow this route, you are going into area 'AreaName'. Then it will subsequently look for views in that area. Your custom ViewEngine will only affect view locations when MVC is looking for them in some area. Otherwise it won't have any effect, as the location format lists for areas are the only ones it overrides.

Answers 2

Try to customize this code for your engine

using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Web; using System.Web.Mvc;  namespace Partikan.Tuba.Core {      /*                                              TubaPortal Razor ViewEngine      *       * Path for Themes : ~/Site/Themes/{ThemeName}/Skin/{Culture}/{SkinName} ---> ~/Site/Themes/Default/Skin/fa-IR/default.csHtml      *  themeName, culture, name, controllerName, modulePath ,area      *       *       *       *       */      public class TubaPortalRazorViewEngine : RazorViewEngine     {         private static readonly List<string> EmptyLocations;          public TubaPortalRazorViewEngine()         {               MasterLocationFormats = new[]                 {                     "~/Site/Themes/{0}/Skin/{1}/{2}.cshtml",                     "~/Site/Themes/{0}/Skin/{2}.cshtml",                     "~/Views/Shared/{2}.cshtml"                 };             PartialViewLocationFormats = new[]                 {                     "~/Views/{1}/{0}.cshtml",                     "~/Views/Shared/{0}.cshtml",                                         "~/Site/Themes/{0}/Container/{2}.cshtml",                     "~/Plugins/{2}/{2}.cshtml",                     "~/Views/{3}/{2}.cshtml",                     "~/Views/Shared/{2}.cshtml"                 };              ViewLocationFormats = new[]                 {                                        "~/Site/Themes/{0}/Container/{2}.cshtml",                     "~/Plugins/{2}/{2}.cshtml",                     "~/Views/{3}/{2}.cshtml",                     "~/Views/Shared/{2}.cshtml"                 };              AreaViewLocationFormats = new[]                 {                     "~/Areas/{5}/Views/{3}/{2}.cshtml",                     "~/Areas/{5}/Views/Shared/{2}.cshtml"                 };             AreaMasterLocationFormats = new[]                 {                     "~/Areas/{5}/Views/{3}/{2}.cshtml",                     "~/Areas/{5}/Views/Shared/{2}.cshtml"                 };             AreaPartialViewLocationFormats = new[]                 {                     "~/Areas/{5}/Views/{3}/{2}.cshtml",                     "~/Areas/{5}/Views/Shared/{2}.cshtml"                 };             }          private static string GetThemeToUse(ControllerContext controllerContext)         {             if (controllerContext.HttpContext.Items == null) return "";             var themeName = controllerContext.HttpContext.Items["themeName"] == null ? "" :                 controllerContext.HttpContext.Items["themeName"] as string;                    return String.Format("{0}", themeName);         }         private static string GetCulture(ControllerContext controllerContext)         {             if (controllerContext.HttpContext.Items != null)             {                  var culture = controllerContext.HttpContext.Items["culture"] as string;                 if (string.IsNullOrEmpty(culture)) culture = "fa-IR";                 return String.Format("{0}", culture);             }             return "fa-IR";         }          protected override bool FileExists(ControllerContext controllerContext, string virtualPath)         {             try             {                 return System.IO.File.Exists(controllerContext.HttpContext.Server.MapPath(virtualPath));             }             catch (HttpException exception)             {                 if (exception.GetHttpCode() != 0x194)                 {                     throw;                 }                 return false;             }             catch             {                 return false;             }         }          public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)         {             var strArray = new List<string>();             var strArray2 = new List<string>();                        if (controllerContext == null)             {                 throw new ArgumentNullException("controllerContext");             }             if (string.IsNullOrEmpty(viewName))             {                 throw new ArgumentException("viewName must be specified.", "viewName");             }                var themeName = GetThemeToUse(controllerContext);              var culture = GetCulture(controllerContext);              var controllerName = controllerContext.RouteData.GetRequiredString("controller");             var modulePath = controllerContext.RouteData.Values.Keys.Contains("ModulePath") ? controllerContext.RouteData.GetRequiredString("ModulePath") : "";             var viewPath = "";              var viewLocation = ViewLocationFormats;             var masterLocation = MasterLocationFormats;             var area = "";             if (controllerContext.RouteData.DataTokens.Keys.Any(x => x.ToLower() == "area"))             {                 area = controllerContext.RouteData.DataTokens.FirstOrDefault(x => x.Key.ToLower() == "area").Value.ToString();                  viewLocation = AreaViewLocationFormats;                 masterLocation = AreaMasterLocationFormats;             }              if (IsExternalWidgetView(viewName))             {                 viewPath = viewName;             }             else             {                 viewPath = GetPath(controllerContext, viewLocation, area, viewName, themeName, controllerName, culture, modulePath, "TubaSite_View", useCache, strArray);             }                var masterPath = GetPath(controllerContext, masterLocation, area, masterName, themeName, controllerName, culture, modulePath, "TubaSite_Master", useCache, strArray2);              if (!string.IsNullOrEmpty(viewPath) && (!string.IsNullOrEmpty(masterPath) || string.IsNullOrEmpty(masterName)))             {                 return new ViewEngineResult(this.CreateView(controllerContext, viewPath, masterPath), this);             }             if (string.IsNullOrEmpty(masterPath) && strArray == null)             {                 throw new Exception(String.Format("Skin {0} {1} in {2} Not Found .", themeName, masterName, masterPath));             }             if (string.IsNullOrEmpty(viewPath))             {                 throw new Exception(String.Format("Page Not Found - {0} {1} {2}", themeName, masterName, masterPath));             }             return new ViewEngineResult(strArray.Union<string>(strArray2));         }         public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)         {             var strArray = new List<string>();             if (controllerContext == null)             {                 throw new ArgumentNullException("controllerContext");             }             if (string.IsNullOrEmpty(partialViewName))             {                 throw new ArgumentException("partialViewName must be specified.", "partialViewName");             }               var themeName = GetThemeToUse(controllerContext);             var modulePath = controllerContext.RouteData.Values.Keys.Contains("ModulePath") ? controllerContext.RouteData.GetRequiredString("ModulePath") : "";              var requiredString = controllerContext.RouteData.GetRequiredString("controller");              var partialViewLocation = PartialViewLocationFormats;             var area = "";             if (controllerContext.RouteData.DataTokens.Keys.Any(x => x.ToLower() == "area"))             {                 area = controllerContext.RouteData.DataTokens.FirstOrDefault(x => x.Key.ToLower() == "area").Value.ToString();                 partialViewLocation = AreaPartialViewLocationFormats.Union(PartialViewLocationFormats).ToArray();             }              var partialViewPath = "";             if (IsExternalWidgetView(partialViewName))             {                 partialViewPath = partialViewName;             }             else             {                 partialViewPath = GetPath(controllerContext, partialViewLocation, area, partialViewName, themeName, requiredString, "fa-IR", modulePath, "TubaSite_Partial", false, strArray);             }             return string.IsNullOrEmpty(partialViewPath) ? new ViewEngineResult(strArray) : new ViewEngineResult(CreatePartialView(controllerContext, partialViewPath), this);         }           private string GetPath(ControllerContext controllerContext, string[] locations, string area,                 string name, string themeName, string controllerName, string culture, string modulePath,                 string cacheKeyPrefix, bool useCache, List<string> searchedLocations)         {             searchedLocations = EmptyLocations;             if (string.IsNullOrEmpty(name))             {                 return string.Empty;             }              if ((locations == null) || (locations.Length == 0))             {                 throw new InvalidOperationException("هـیچ مسـیـری بـرای جستجو تعریف نشده است.");             }              var flag = IsSpecificPath(name);             var key = CreateCacheKey(cacheKeyPrefix, name, flag ? string.Empty : controllerName, themeName);             if (useCache)             {                 var viewLocation = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, key);                 if (viewLocation != null)                 {                     return viewLocation;                 }             }             if (!flag)             {                 return string.IsNullOrEmpty(culture) ?                         GetPathFromGeneralName(controllerContext, locations, area, name, controllerName, themeName,  modulePath, key, searchedLocations) :                         GetPathFromGeneralName(controllerContext, locations, area, name, controllerName, themeName, culture, modulePath, key, searchedLocations);              }             return GetPathFromSpecificName(controllerContext, name, key, searchedLocations);         }          private static bool IsExternalWidgetView(string name)         {              return name.StartsWith("~/ExternalWidget", StringComparison.InvariantCultureIgnoreCase);         }          private static bool IsSpecificPath(string name)         {             var ch = name[0];             if (ch != '~')             {                 return (ch == '/');             }             return true;         }          private string CreateCacheKey(string prefix, string name, string controllerName, string themeName)         {             return string.Format(CultureInfo.InvariantCulture,                     ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}", GetType().AssemblyQualifiedName, prefix, name, controllerName, themeName);         }          private string GetPathFromGeneralName(ControllerContext controllerContext, IEnumerable<string> locations, string area, string name,                 string controllerName, string themeName,  string culture, string modulePath, string cacheKey, List<string> searchedLocations)         {             if (locations == null) throw new ArgumentNullException("locations");             if (searchedLocations == null) searchedLocations = new List<string>();              var virtualPath = string.Empty;                      var locationData =                 locations.Select(                     t =>                     string.Format(CultureInfo.InvariantCulture, t,                                                 string.IsNullOrEmpty(culture)                                                     ? new object[] { name, controllerName }                                                     : new object[] {  themeName, culture, name, controllerName, modulePath, area })).ToList();             foreach (var str2 in locationData)             {                 if (FileExists(controllerContext, str2))                 {                     virtualPath = str2;                     ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);                     return virtualPath;                 }                 searchedLocations.Add(str2);             }             return virtualPath;         }          private string GetPathFromGeneralName(ControllerContext controllerContext, IEnumerable<string> locations, string area, string name,                 string controllerName, string themeName,  string modulePath, string cacheKey, List<string> searchedLocations)         {             return GetPathFromGeneralName(controllerContext, locations, area, name, controllerName, themeName, "", modulePath, cacheKey, searchedLocations);         }          private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, List<string> searchedLocations)         {             var virtualPath = name;             if (!FileExists(controllerContext, name))             {                 virtualPath = string.Empty;                 searchedLocations = new List<string>() { name };             }             ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);             return virtualPath;         }     }  } 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment