2012-11-29 36 views
2

我已经使自己成为一个模块化框架与ninject为MVC。布局在模块化的MVC应用程序

每个模块都可以注册自己的路线并包含它自己的视图。

模块DIR(DLL位置):
~/Modules/<module name>/

模块视图坐在里面:
<Module dir>/Views/
它们被布置酷似正常MVC应用程序,即,对于每个控制器和一个共享文件夹的文件夹。

我想呈现一个布局的视图,但是我希望布局位置由核心框架设置(以便我可以更改主题)。

我有了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

如何模块来实现的?它们是作为MVC领域实现的吗? –

+0

他们本质上是注册控制器和其他任何内核ninject模块。自定义控制器工厂通过内核查找控制器。每个模块都在一个单独的程序集中。 – MrJD

+0

你解决了吗?我目前遇到同样的问题。 – ngm

回答

1

这花了大量的工作。

必须对视图引擎进行更改才能正确公开FindViewFindPartialView方法。问题中概述的方法是错误的。

这是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; 
     } 
    } 
} 

希望帮助:)

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()); 
+0

我有一个自定义视图引擎,我将编辑问题以包含代码 – MrJD

0

你真的应该尝试使用预编译的意见与RazorGenerator

编译视图允许您将模块作为单个DLL进行放置,这比将所有内容(如cshtml视图)带入DLL要容易得多,您可以在启动时加载模块或使用MEF或任何其他的反射机制,使您的MVC应用程序真正模块化,并减少耦合。

我发现自己这个实现在模块化网站上不具有成本效益。