यह एक मेरी दिलचस्पी थी, और मुझे आखिरकार इस पर गौर करने का मौका मिला। अन्य लोगों को स्पष्ट रूप से समझ में नहीं आया है कि यह देखने के साथ एक मुद्दा है , न कि रूटिंग के साथ एक मुद्दा है - और शायद यही कारण है कि आपका प्रश्न शीर्षक इंगित करता है कि यह रूटिंग के बारे में है।
किसी भी मामले में, क्योंकि यह एक दृश्य-संबंधित मुद्दा है, जो आप चाहते हैं उसे प्राप्त करने का एकमात्र तरीका डिफ़ॉल्ट दृश्य इंजन को ओवरराइड करना है । आम तौर पर, जब आप ऐसा करते हैं, तो यह आपके दृश्य इंजन (यानी स्पार्क, एनएचएएमएल, आदि) को स्विच करने के सरल उद्देश्य के लिए है। इस मामले में, यह दृश्य-निर्माण तर्क नहीं है जिसे हमें ओवरराइड करने की आवश्यकता है, लेकिन कक्षा में FindPartialView
और FindView
विधियां VirtualPathProviderViewEngine
।
आप अपने भाग्यशाली सितारों को धन्यवाद दे सकते हैं कि ये विधियां वास्तव में आभासी हैं, क्योंकि बाकी सब कुछ VirtualPathProviderViewEngine
भी सुलभ नहीं है - यह निजी है, और यह खोज तर्क को ओवरराइड करने के लिए बहुत परेशान करता है क्योंकि आपको मूल रूप से पहले से मौजूद कोड के आधे को फिर से लिखना होगा। यदि आप इसे स्थान कैश और स्थान स्वरूपों के साथ अच्छा खेलना चाहते हैं तो लिखा गया है। रिफ्लेक्टर में कुछ खुदाई के बाद मैं आखिरकार एक काम करने वाले समाधान के साथ आने में कामयाब रहा।
मैंने यहां जो कुछ किया है वह पहले एक अमूर्त बनाना है जो इसके बजाय AreaAwareViewEngine
सीधे से प्राप्त होता VirtualPathProviderViewEngine
है WebFormViewEngine
। मैंने ऐसा इसलिए किया ताकि यदि आप (या जो भी हो) स्पार्क विचार बनाना चाहते हैं, तब भी आप इस वर्ग को आधार प्रकार के रूप में उपयोग कर सकते हैं।
नीचे दिया गया कोड बहुत लंबा-घुमावदार है, इसलिए आपको यह वास्तव में क्या करता है इसका एक त्वरित सारांश देने के लिए: यह आपको {2}
स्थान प्रारूप में डाल देता है , जो क्षेत्र के नाम से {1}
मेल खाता है , उसी तरह नियंत्रक नाम से मेल खाता है। बस! यही कारण है कि हमें यह सब कोड लिखना पड़ा है:
BaseAreaAwareViewEngine.cs
public abstract class BaseAreaAwareViewEngine : VirtualPathProviderViewEngine
{
private static readonly string[] EmptyLocations = { };
public override ViewEngineResult FindView(
ControllerContext controllerContext, string viewName,
string masterName, bool useCache)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(viewName))
{
throw new ArgumentNullException(viewName,
"Value cannot be null or empty.");
}
string area = getArea(controllerContext);
return FindAreaView(controllerContext, area, viewName,
masterName, useCache);
}
public override ViewEngineResult FindPartialView(
ControllerContext controllerContext, string partialViewName,
bool useCache)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(partialViewName))
{
throw new ArgumentNullException(partialViewName,
"Value cannot be null or empty.");
}
string area = getArea(controllerContext);
return FindAreaPartialView(controllerContext, area,
partialViewName, useCache);
}
protected virtual ViewEngineResult FindAreaView(
ControllerContext controllerContext, string areaName, string viewName,
string masterName, bool useCache)
{
string controllerName =
controllerContext.RouteData.GetRequiredString("controller");
string[] searchedViewPaths;
string viewPath = GetPath(controllerContext, ViewLocationFormats,
"ViewLocationFormats", viewName, controllerName, areaName, "View",
useCache, out searchedViewPaths);
string[] searchedMasterPaths;
string masterPath = GetPath(controllerContext, MasterLocationFormats,
"MasterLocationFormats", masterName, controllerName, areaName,
"Master", useCache, out searchedMasterPaths);
if (!string.IsNullOrEmpty(viewPath) &&
(!string.IsNullOrEmpty(masterPath) ||
string.IsNullOrEmpty(masterName)))
{
return new ViewEngineResult(CreateView(controllerContext, viewPath,
masterPath), this);
}
return new ViewEngineResult(
searchedViewPaths.Union<string>(searchedMasterPaths));
}
protected virtual ViewEngineResult FindAreaPartialView(
ControllerContext controllerContext, string areaName,
string viewName, bool useCache)
{
string controllerName =
controllerContext.RouteData.GetRequiredString("controller");
string[] searchedViewPaths;
string partialViewPath = GetPath(controllerContext,
ViewLocationFormats, "PartialViewLocationFormats", viewName,
controllerName, areaName, "Partial", useCache,
out searchedViewPaths);
if (!string.IsNullOrEmpty(partialViewPath))
{
return new ViewEngineResult(CreatePartialView(controllerContext,
partialViewPath), this);
}
return new ViewEngineResult(searchedViewPaths);
}
protected string CreateCacheKey(string prefix, string name,
string controller, string area)
{
return string.Format(CultureInfo.InvariantCulture,
":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:",
base.GetType().AssemblyQualifiedName,
prefix, name, controller, area);
}
protected string GetPath(ControllerContext controllerContext,
string[] locations, string locationsPropertyName, string name,
string controllerName, string areaName, string cacheKeyPrefix,
bool useCache, out string[] searchedLocations)
{
searchedLocations = EmptyLocations;
if (string.IsNullOrEmpty(name))
{
return string.Empty;
}
if ((locations == null) || (locations.Length == 0))
{
throw new InvalidOperationException(string.Format("The property " +
"'{0}' cannot be null or empty.", locationsPropertyName));
}
bool isSpecificPath = IsSpecificPath(name);
string key = CreateCacheKey(cacheKeyPrefix, name,
isSpecificPath ? string.Empty : controllerName,
isSpecificPath ? string.Empty : areaName);
if (useCache)
{
string viewLocation = ViewLocationCache.GetViewLocation(
controllerContext.HttpContext, key);
if (viewLocation != null)
{
return viewLocation;
}
}
if (!isSpecificPath)
{
return GetPathFromGeneralName(controllerContext, locations, name,
controllerName, areaName, key, ref searchedLocations);
}
return GetPathFromSpecificName(controllerContext, name, key,
ref searchedLocations);
}
protected string GetPathFromGeneralName(ControllerContext controllerContext,
string[] locations, string name, string controllerName,
string areaName, string cacheKey, ref string[] searchedLocations)
{
string virtualPath = string.Empty;
searchedLocations = new string[locations.Length];
for (int i = 0; i < locations.Length; i++)
{
if (string.IsNullOrEmpty(areaName) && locations[i].Contains("{2}"))
{
continue;
}
string testPath = string.Format(CultureInfo.InvariantCulture,
locations[i], name, controllerName, areaName);
if (FileExists(controllerContext, testPath))
{
searchedLocations = EmptyLocations;
virtualPath = testPath;
ViewLocationCache.InsertViewLocation(
controllerContext.HttpContext, cacheKey, virtualPath);
return virtualPath;
}
searchedLocations[i] = testPath;
}
return virtualPath;
}
protected string GetPathFromSpecificName(
ControllerContext controllerContext, string name, string cacheKey,
ref string[] searchedLocations)
{
string virtualPath = name;
if (!FileExists(controllerContext, name))
{
virtualPath = string.Empty;
searchedLocations = new string[] { name };
}
ViewLocationCache.InsertViewLocation(controllerContext.HttpContext,
cacheKey, virtualPath);
return virtualPath;
}
protected string getArea(ControllerContext controllerContext)
{
// First try to get area from a RouteValue override, like one specified in the Defaults arg to a Route.
object areaO;
controllerContext.RouteData.Values.TryGetValue("area", out areaO);
// If not specified, try to get it from the Controller's namespace
if (areaO != null)
return (string)areaO;
string namespa = controllerContext.Controller.GetType().Namespace;
int areaStart = namespa.IndexOf("Areas.");
if (areaStart == -1)
return null;
areaStart += 6;
int areaEnd = namespa.IndexOf('.', areaStart + 1);
string area = namespa.Substring(areaStart, areaEnd - areaStart);
return area;
}
protected static bool IsSpecificPath(string name)
{
char ch = name[0];
if (ch != '~')
{
return (ch == '/');
}
return true;
}
}
अब जैसा कि कहा गया है, यह एक ठोस इंजन नहीं है, इसलिए आपको इसे भी बनाना होगा। यह हिस्सा, सौभाग्य से, है बहुत आसान है, हमें बस इतना करना है कि डिफ़ॉल्ट प्रारूप सेट करें और वास्तव में विचार बनाएं:
AreaAwareViewEngine.cs
public class AreaAwareViewEngine : BaseAreaAwareViewEngine
{
public AreaAwareViewEngine()
{
MasterLocationFormats = new string[]
{
"~/Areas/{2}/Views/{1}/{0}.master",
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.master",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
"~/Views/{1}/{0}.master",
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.master"
"~/Views/Shared/{0}.cshtml"
};
ViewLocationFormats = new string[]
{
"~/Areas/{2}/Views/{1}/{0}.aspx",
"~/Areas/{2}/Views/{1}/{0}.ascx",
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.aspx",
"~/Areas/{2}/Views/Shared/{0}.ascx",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.aspx"
"~/Views/Shared/{0}.ascx"
"~/Views/Shared/{0}.cshtml"
};
PartialViewLocationFormats = ViewLocationFormats;
}
protected override IView CreatePartialView(
ControllerContext controllerContext, string partialPath)
{
if (partialPath.EndsWith(".cshtml"))
return new System.Web.Mvc.RazorView(controllerContext, partialPath, null, false, null);
else
return new WebFormView(controllerContext, partialPath);
}
protected override IView CreateView(ControllerContext controllerContext,
string viewPath, string masterPath)
{
if (viewPath.EndsWith(".cshtml"))
return new RazorView(controllerContext, viewPath, masterPath, false, null);
else
return new WebFormView(controllerContext, viewPath, masterPath);
}
}
ध्यान दें कि हमने कुछ प्रविष्टियाँ मानक में जोड़ दी हैं ViewLocationFormats
। ये नई {2}
प्रविष्टियां हैं, जहां हम डाल में {2}
मैप किए जाएंगेarea
RouteData
। मैंने MasterLocationFormats
अकेला छोड़ दिया है , लेकिन जाहिर है कि आप चाहें तो बदल सकते हैं।
अब अपने को संशोधित करें global.asax
इस दृश्य इंजन को पंजीकृत को :
Global.asax.cs
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new AreaAwareViewEngine());
}
... और डिफ़ॉल्ट मार्ग पंजीकृत करें:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Area",
"",
new { area = "AreaZ", controller = "Default", action = "ActionY" }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
}
अब बनाएँ AreaController
हम सिर्फ संदर्भित :
DefaultController.cs (~ / नियंत्रक / में)
public class DefaultController : Controller
{
public ActionResult ActionY()
{
return View("TestView");
}
}
जाहिर है कि हमें निर्देशिका संरचना और उसके साथ जाने के लिए देखने की आवश्यकता है - हम इसे सुपर सरल रखेंगे:
TestView.aspx (~ में / क्षेत्रों / क्षेत्र / दृश्य / डिफ़ॉल्ट / या ~ / क्षेत्र / क्षेत्र / दृश्य / साझा /)
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<h2>TestView</h2>
This is a test view in AreaZ.
और बस। अंत में, हम कर रहे हैं ।
अधिकांश भाग के लिए, आप बस लेने के लिए सक्षम होना चाहिए BaseAreaAwareViewEngine
और AreaAwareViewEngine
और किसी भी MVC परियोजना में छोड़ने, तो भले ही यह कोड का एक बहुत ले लिया इस किया पाने के लिए, आप केवल एक बार लिखने के लिए की है। उसके बाद, यह केवल कुछ लाइनों को संपादित करने global.asax.cs
और आपकी साइट संरचना बनाने की बात है ।