2012-02-08 58 views
5

我在單獨的類庫中使用編譯的Razor視圖作爲MVC3的一種插件系統。MVC3 - 編譯的剃刀視圖找不到_ViewStart

我已經按照Chris Van De Steed here的指導進行了操作,只是主要關於添加引用的部分,因爲我在運行時加載了我的程序集。

因爲我在運行時加載程序集,所以我沒有在BoC庫中使用VirtualPathProviderViewEngine,而是實現了基於RazorViewEngine的自己的ViewEngine。它通過在CreateView中重寫viewPath來插入適當的名稱空間,以便解析視圖。

到目前爲止這麼好...我可以加載不同的模塊,如果他們的控制器共享相同的名稱,他們的控制器不會發生衝突。

我現在唯一的問題是,對於已編譯的視圖,_ViewStart不會被調用。 _ViewStart適用於主機MVC3項目中的視圖,但對於從插件程序集加載的任何視圖,都不會找到它。

我有一個路徑設定是這樣的: -

RouteTable.Routes.MapRoute(
    string.Format("Plugin{0}Route", pluginName), 
    string.Format(@"Plugin/{0}/{{controller}}/{{action}}", pluginName), 
    new { }, 
    new string[] { string.Format("{0}.Controllers", pluginName) }); 

ViewEngine可以看起來像這樣: -

public class PluginRazorViewEngine : RazorViewEngine 
{ 
    public PluginRazorViewEngine() : base() 
    { 
     ViewLocationFormats = new[] 
     { 
      "~/Plugin/%1/Views/{1}/{0}.cshtml", 
      "~/Plugin/%1/Views/{1}/{0}.vbhtml", 
      "~/Plugin/%1/Views/Shared/{0}.cshtml", 
      "~/Plugin/%1/Views/Shared/{0}.vbhtml", 
      "~/Views/{1}/{0}.cshtml", 
      "~/Views/{1}/{0}.vbhtml", 
      "~/Views/Shared/{0}.cshtml", 
      "~/Views/Shared/{0}.vbhtml" 
     }; 

(%1替換爲組件的名稱)

並且程序集是這樣註冊的BoC庫: -

BoC.Web.Mvc.PrecompiledViews.ApplicationPartRegistry.Register(assembly, string.Format("/Plugin/{0}/", pluginName)); 

當從插件程序集(在本例中爲「accounts」)加載視圖時,視圖被找到並顯示OK。但隨後它會在這些位置的_ViewStart: -

~/plugin/accounts/views/invoice/_viewstart.cshtml 
~/plugin/accounts/views/invoice/_viewstart.vbhtml 
~/plugin/accounts/views/_viewstart.cshtml 
~/plugin/accounts/views/_viewstart.vbhtml 
~/plugin/accounts/_viewstart.cshtml 
~/plugin/accounts/_viewstart.vbhtml 
~/plugin/_viewstart.cshtml 
~/plugin/_viewstart.vbhtml 
~/_viewstart.cshtml 
~/_viewstart.vbhtml 

但它不看〜/查看/共享/ _ViewStart.cshtml文件的生活在那裏。

我試着改變我的ViewEngine(AreaMasterLocationFormats,AreaPartialViewLocationFormats,AreaViewLocationFormats,MasterLocationFormats,PartialViewLocationFormats和ViewLocationFormats)中的所有位置格式,但它們都沒有改變。

我環顧四周,似乎System.Web.WebPages.StartPage.GetStartPage負責查找並返回視圖中的起始頁面,但我找不到任何有關如何控制它的外觀的信息。

我已經試過移動_ViewStart.cshtml到〜/ _ViewStart.cshtml(它看起來的地方之一),但是我立刻得到: -

Unable to cast object of type 'ASP._Page__ViewStart_cshtml' to type 'System.Web.WebPages.StartPage'. 

它根據我讀過,是因爲_ViewStart需要生活在/ Views

我可以修改MVC在哪裏查找_ViewStart嗎?

的中行庫實現它自己的iView,並要求如下: -

startPage = this.StartPageLookup(page, VirtualPathFactoryManagerViewEngine.ViewStartFileName, this.ViewStartFileExtensions); 

但在這種情況下ViewStartFileName只是「_ViewStart」和ViewStartFileExtensions只是CSHTML和vbhtml ......沒有什麼會控制在何處MVC應該搜索文件。

回答

1

一個想法......(作中,還沒有嘗試過將它的工作不知道?)

也許看看從RazorView繼承(或完全取代它,考慮 - 因爲我們見 - y你會重寫這個類的大部分方法)。

RazorView就是StartPage.GetStartPage被分配給一個StartPageLookup財產的方式介紹:

// In RazorView constructor: 
StartPageLookup = StartPage.GetStartPage; 

不幸的是,該委託財產是內部的,所以你不能只是簡單地覆蓋在你的派生類的構造函數。您可能,但是,能夠覆蓋RazorView.RenderView,這就是它的使用(MVC3源代碼,很多線路的去除,換行符由我添加的):

protected override void RenderView(ViewContext viewContext, TextWriter writer, 
    object instance) 
{ 
    // [SNIP] 

    WebPageRenderingBase startPage = null; 
    if (RunViewStartPages) { 
    // HERE IT IS: 
    startPage = StartPageLookup(
     webViewPage, 
     RazorViewEngine.ViewStartFileName, 
     ViewStartFileExtensions 
    ); 
    } 
    webViewPage.ExecutePageHierarchy(
    new WebPageContext(
     context: viewContext.HttpContext, 
     page: null, 
     model: null), 
    writer, startPage); 
} 

替換StartPageLookup通話用自己的查找,然後將PluginRazorViewEngine中的CreateViewCreatePartialView的結果替換爲新的PluginRazorView類。

+0

我照你的建議做了,並創建了我自己的從RazorView繼承的「PluginRazorView」。 我不得不按照你的建議重寫RenderView,我試圖複製MVC GetStartPage方法所做的事情,只是添加我自己的邏輯來搜索額外的路徑,但是它是內部和反射的雷區,所以我只是一起黑客攻擊證明它的工作原理,我將不得不在晚些時候清理它。 有趣的是,他們在這裏使用了一個委託......似乎可以使用不同的搜索方法,但是沒有辦法指定它們。 非常感謝您的幫助。 – Aleks 2012-02-09 11:39:11

+0

是的,這也是我的想法。就像你說的,他們使用一個委託來允許改變查找方法,但是然後聲明它是'internal',所以它只是Mvc命名空間,它實際上可以改變它... – JimmiTh 2012-02-09 13:50:48

1

因此,要回答我的問題......好像沒有,你不能修改其中MVC將查找_ViewStart ...

望着System.Web.WebPages.StartPage的來源。 GetStartPage(我從here得到)我可以看到它只遍歷從調用頁面一直到根的路徑,似乎沒有任何方法來控制這種行爲(即它是硬編碼的System.Web.WebPages.StartPage)

在正常情況下(即標準的MVC3佈局),這將是OK ...所有視圖都位於/ Views下,而主_ViewStart文件也是這樣,當GetStartPage到達它。

所以我基本上做的是通過將視圖移出/ Views文件夾層次來打破這個功能。

我想這意味着我可以將我的_ViewStart文件移動到一個可以找到它的位置(哪種打破了我的所有插件的公共_ViewStart的預期目標),或者找出某種重寫方式/將該層次結構中_ViewStart的請求重定向到/ Views/Shared/_ViewStart中的正確文件(這對我來說並不是很明顯)。

有趣的是,MVC3代碼將尋找_ViewStart在/,這基於我讀過將無法正常工作,導致「無法投類型的對象ASP。 _ViewStart_cshtml「來鍵入'System.Web.WebPages.StartPage'「錯誤(儘管我懷疑這只是因爲默認的/web.config沒有必要的東西來正確解析文件,而/Views/web.config)