我自己制作了一个带有ninject for MVC的模块化框架。
每个模块都可以注册自己的路径,并包含自己的视图。
模块目录(dll位置):
~/Modules/<module name>/
模块视图位于内部:
<Module dir>/Views/
它们的排列方式与普通的mvc应用程序完全相同,IE是每个控制器的文件夹和共享文件夹。
我想用布局渲染视图,但我希望布局位置由核心框架设置(以便我可以更改主题)。
我的视图有layout = _layout.cshtml
,当我运行应用程序时,它会返回:
The layout page "_Layout.cshtml" could not be found at the following path: "~/Modules/Module2/Views/Home/_Layout.cshtml".
此处调用的视图位于~/Modules/Module2/Views/Home/Index.cshtml
。但我希望它在另一个位置查找布局,而不在每个视图中设置。无论如何我能在核心框架中做到这一点吗?注意我将它设置为MasterLocationFormats以查看共享,它显然没有(我通过在其中放置_layout.cshtml来测试)。
自定义视图引擎:
public NinjectRazorViewEngine(): base()
{
ViewLocationFormats = new[] {
"~/Modules/%1/Views/{1}/{0}.cshtml",
"~/Modules/%1/Views/{1}/{0}.vbhtml",
"~/Modules/%1/Views/Shared/{0}.cshtml",
"~/Modules/%1/Views/Shared/{0}.vbhtml"
};
MasterLocationFormats = new[] {
"~/Modules/%1/Views/{1}/{0}.cshtml",
"~/Modules/%1/Views/{1}/{0}.vbhtml",
"~/Modules/%1/Views/Shared/{0}.cshtml",
"~/Modules/%1/Views/Shared/{0}.vbhtml",
};
PartialViewLocationFormats = new[] {
"~/Modules/%1/Views/{1}/{0}.cshtml",
"~/Modules/%1/Views/{1}/{0}.vbhtml",
"~/Modules/%1/Views/Shared/{0}.cshtml",
"~/Modules/%1/Views/Shared/{0}.vbhtml"
};
PartialViewLocationFormats = ViewLocationFormats;
AreaPartialViewLocationFormats = AreaViewLocationFormats;
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
object moduleName;
if(controllerContext.RequestContext.RouteData.Values.TryGetValue("module",out moduleName))
return base.CreatePartialView(controllerContext, partialPath.Replace("%1", (string)moduleName));
return base.CreatePartialView(controllerContext, partialPath);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
object moduleName;
if (controllerContext.RequestContext.RouteData.Values.TryGetValue("module", out moduleName))
return base.CreateView(controllerContext, viewPath.Replace("%1", (string)moduleName), masterPath.Replace("%1", (string)moduleName));
return base.CreateView(controllerContext, viewPath, masterPath);
}
protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
{
object moduleName;
if (controllerContext.RequestContext.RouteData.Values.TryGetValue("module", out moduleName))
return base.FileExists(controllerContext, virtualPath.Replace("%1", (string)moduleName));
return base.FileExists(controllerContext, virtualPath);
}
答案 0 :(得分:1)
这花了很多工作。
必须对视图引擎进行更改才能正确公开FindView
和FindPartialView
方法。问题中概述的方法是错误的。
这是viewEngineClass的外观
public NinjectRazorViewEngine(): base()
{
ViewLocationFormats = new[] {
"~/Modules/{2}/Views/{1}/{0}.cshtml",
"~/Modules/{2}/Views/{1}/{0}.vbhtml",
"~/Modules/{2}/Views/Shared/{0}.cshtml",
"~/Modules/{2}/Views/Shared/{0}.vbhtml",
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
};
MasterLocationFormats = new[] {
"~/Modules/{2}/Views/{1}/{0}.cshtml",
"~/Modules/{2}/Views/{1}/{0}.vbhtml",
"~/Modules/{2}/Views/Shared/{0}.cshtml",
"~/Modules/{2}/Views/Shared/{0}.vbhtml",
};
PartialViewLocationFormats = new[] {
"~/Modules/{2}/Views/{1}/{0}.cshtml",
"~/Modules/{2}/Views/{1}/{0}.vbhtml",
"~/Modules/{2}/Views/Shared/{0}.cshtml",
"~/Modules/{2}/Views/Shared/{0}.vbhtml",
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
};
PartialViewLocationFormats = ViewLocationFormats;
AreaPartialViewLocationFormats = AreaViewLocationFormats;
//Used to test cache
//ViewLocationCache = new DefaultViewLocationCache();
}
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
return FindView(controllerContext, partialViewName, "", useCache);
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
//Implement defualt exceptions
if(controllerContext == null)
throw new ArgumentNullException("The controllerContext parameter is null");
if(string.IsNullOrEmpty(viewName))
throw new ArgumentException("The viewName parameter is null or empty.");
//Check cache if specified
if(useCache && this.ViewLocationCache != null){
string cachedLocation = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, generateCacheKey(controllerContext, viewName));
if (!string.IsNullOrEmpty(cachedLocation))
return new ViewEngineResult(CreateView(controllerContext, cachedLocation, masterName), this);
}
//Create arguments for location formatting
string trimmedViewName = string.Empty;
if (viewName.EndsWith(".cshtml"))
trimmedViewName = viewName.Remove(viewName.Length - 7);
else
trimmedViewName = viewName;
object[] args = new object[] { trimmedViewName, controllerContext.RouteData.GetRequiredString("controller"), controllerContext.RouteData.GetRequiredString("module") };
//Attempt to locate file
List<string> searchedLocations = new List<string>();
foreach(string location in ViewLocationFormats){
string formatedLocation = string.Format(location,args);
searchedLocations.Add(formatedLocation);
if (FileExists(controllerContext, formatedLocation))
{
//File has been found. Add to cache and return view
if(this.ViewLocationCache != null)
ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, generateCacheKey(controllerContext, viewName), formatedLocation);
return new ViewEngineResult(CreateView(controllerContext, formatedLocation, masterName), this);
}
}
//Couldnt find view, return searched locations
return new ViewEngineResult(searchedLocations);
}
public string generateCacheKey(ControllerContext controllerContext, string viewName)
{
return string.Format("{0}|{1}", controllerContext.RouteData.GetRequiredString("module"), viewName);
}
然后您需要实现自定义System.Web.Mvc.WebViewPage<T>
,如下所示:
public abstract class WebViewPage<T> : System.Web.Mvc.WebViewPage<T>
{
public override string Layout
{
get
{
return base.Layout;
}
set
{
NinjectRazorViewEngine viewEngine = new NinjectRazorViewEngine();
System.Web.Mvc.ViewEngineResult engineResult = viewEngine.FindView(this.ViewContext.Controller.ControllerContext, value, string.Empty, true);
System.Web.Mvc.RazorView razorView = engineResult.View as System.Web.Mvc.RazorView;
if (razorView == null)
{
string searchedIn = "";
foreach (string item in engineResult.SearchedLocations)
{
searchedIn += item + "\n";
}
throw new HttpException(500, "Could not find views in locations:\n" + searchedIn);
}
base.Layout = razorView.ViewPath;
}
}
}
希望有所帮助:)
答案 1 :(得分:0)
您可以实现自己的ViewEngine,它将在自定义位置查找视图。
public class MyViewEngine : RazorViewEngine {
public MyViewEngine() {
this.MasterLocationFormats = new string[] {
"PATH TO YOUR LAYOUT FILES", "ALTERNATIVE PATH"
}
}
}
然后在启动应用程序期间(例如在Global.asax.cs中)设置应用程序以使用您的自定义引擎
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new ThemableViewEngine());
答案 2 :(得分:0)
您应该尝试使用RazorGenerator预编译的视图。
编译视图允许您将模块作为单个DLL删除,这比使用DLL所有内容(如cshtml视图)更容易加上,您可以在启动时或运行时使用MEF或任何加载模块其他反射机制,使你的MVC应用程序真正模块化,减少耦合。
我发现自己这种实施方式在模块化网站上并不划算。