我在单独的类库中使用已编译的Razor视图作为MVC3的一种插件系统。
我已经按照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”),查找并显示视图。但是它会在这些位置查找_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
但它没有查看文件所在的〜/ Views / Shared / _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的位置吗?
BoC库实现了它自己的IView,并调用以下内容: -
startPage = this.StartPageLookup(page, VirtualPathFactoryManagerViewEngine.ViewStartFileName, this.ViewStartFileExtensions);
但在这种情况下,ViewStartFileName只是“_ViewStart”而ViewStartFileExtensions只是cshtml和vbhtml ......没有任何东西可以控制MVC搜索文件的位置。
答案 0 :(得分:1)
所以,回答我自己的问题......似乎没有,你无法修改MVC寻找_ViewStart的位置......
查看System.Web.WebPages.StartPage.GetStartPage的源代码(我从here获得)我可以看到它只遍历从调用页面一直到根目录的路径,并且似乎没有任何方法可以控制这种行为(即它在System.Web.WebPages.StartPage中是硬编码的)
在正常情况下(即标准MVC3布局),这样就可以了......所有视图都位于/ Views之下,主_ViewStart文件也是如此,当GetStartPage到达时,它将被评估。
所以我基本上完成的是通过将我的视图移出/ Views文件夹层次结构来破坏此功能。
我想这意味着我可以将我的_ViewStart文件移动到一个可以找到它的位置(哪种打破了我的所有插件的常见_ViewStart的预期目标)或者找出一些重写方式/将该层次结构中_ViewStart的请求重定向到/ Views / Shared / _ViewStart中的正确文件(这对我来说并不是很明显)。
有趣的是MVC3代码会在/中查找_ViewStart,它基于我读过的内容不起作用,导致“无法转换类型'ASP的对象。 Page _ViewStart_cshtml'键入'System.Web.WebPages.StartPage'“错误(虽然我怀疑这只是因为默认的/web.config没有必要的东西来正确解析文件,而/ Views / web .config确实)
答案 1 :(得分:1)
一个想法......(因为,没有尝试过。它会起作用吗?不知道)
也许看看从RazorView
继承(或者完全替换它,考虑 - 正如我们将看到的那样 - 你将重写一个方法,这是该类的大部分)。
RazorView
是通过将StartPage.GetStartPage
属性分配给StartPageLookup
来引入// In RazorView constructor:
StartPageLookup = StartPage.GetStartPage;
的地方:
RazorView.RenderView
不幸的是,该委托属性是内部的,因此您不能仅在派生类的构造函数中覆盖它。但是,您可以覆盖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);
}
,这是使用的地方(MVC3源代码,删除了大量行,我添加了换行符):
CreateView
将StartPageLookup调用替换为您自己的查找,然后将CreatePartialView
中的PluginRazorViewEngine
和PluginRazorView
的结果替换为新的{{1}}类。