2012-11-29 37 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應用程序真正模塊化,並減少耦合。

我發現自己這個實現在模塊化網站上不具有成本效益。