ASP.NET MVC 视图引擎解析顺序
我创建了一个简单的 ASP.NET MVC 1.0 版应用程序。我有一个 ProductController,它有一个操作索引。在视图中,我在Product子文件夹下创建了相应的Index.aspx。
然后我引用了 Spark dll 并在同一产品视图文件夹下创建了 Index.spark。 Application_Start 看起来像
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new Spark.Web.Mvc.SparkViewFactory());
ViewEngines.Engines.Add(new WebFormViewEngine());
}
我的期望是,由于 Spark 引擎在默认 WebFormViewEngine 之前注册,因此当浏览 Product 控制器中的 Index 操作时,应使用 Spark 引擎,并且应将 WebFormViewEngine 用于所有其他 url。
但是,测试表明 Product 控制器的 Index 操作也使用 WebFormViewEngine。
如果我注释掉 WebFormViewEnginer 的注册(代码中的最后一行),我可以看到 Index 操作是由 Spark 引擎渲染的,其余 url 会生成错误(因为默认引擎已经消失),这证明我的所有Spark代码是正确的。
现在我的问题是视图引擎是如何解决的?为什么注册顺序不生效?
I created a simple ASP.NET MVC version 1.0 application. I have a ProductController which has one action Index. In the view, I created a corresponding Index.aspx under Product subfolder.
Then I referenced the Spark dll and created Index.spark under the same Product view folder. The Application_Start looks like
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new Spark.Web.Mvc.SparkViewFactory());
ViewEngines.Engines.Add(new WebFormViewEngine());
}
My expectation is that since the Spark engine registers before default WebFormViewEngine, when browse the Index action in Product controller, the Spark engine should be used, and WebFormViewEngine should be used for all other urls.
However, the test shows that the Index action for Product controller also uses the WebFormViewEngine.
If I comment out the registration of WebFormViewEnginer (the last line in the code), I can see that the Index action is rendered by Spark engine and the rest urls generates an error (since the defualt engine is gone), it proves that all my Spark code is correct.
Now my question is how the view engine is resolved? Why the registration sequence does not take effect?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
注册视图引擎的顺序并不重要。相反,视图引擎采用一组
ViewLocationFormats
,如果特定视图路径符合格式化名称,则将使用该引擎。仅当格式冲突时,注册顺序才重要。对于 Spark,视图应具有
.spark
扩展名。WebFormViewEngine
将响应任何具有.aspx
或.ascx
扩展名的内容。当然,如上所述,您可以通过更改提供给各个视图引擎的ViewLocationFormats
来覆盖其中的任何内容。更新:
我查看了
SparkViewFactory
和WebFormViewEngine
(或更具体地说,VirtualPathProviderViewEngine
)的源代码,其中后者源自),我可以告诉你为什么你会看到这种奇怪的行为。首先,
ViewEngineCollection
类中的Find
方法的工作原理如下(简化版):换句话说,它将始终尝试在 中查找缓存的视图。任何引擎,然后再诉诸非缓存模式。
各个视图引擎实现此功能的方式是 FindView 方法的第二次重载,该方法采用名为 useCache 的 bool 参数。
但是,这就是一切变得奇怪的地方 -
VirtualPathProviderViewEngine
和SparkViewEngine
对useCache
的想法有非常不同的想法论证的意思。这里有太多代码需要重新发布,但基本思想是:如果
useCache
为,
SparkViewFactory
将仅在缓存中查找>真实。如果它没有找到任何东西,它会自动返回一个“缓存未命中结果”——即什么也没有。另一方面,如果useCache
为false
,它根本不会在缓存中查找,它将跳过缓存检查步骤并通过正常的动作来解决并创建一个实际视图。另一方面,如果
useCache
为true
,则VirtualPathProviderViewEngine
会在缓存中查找,如果没有找到视图在缓存中,它会关闭并创建一个新的并将其添加到缓存中。这两种方法都与
ViewEngineCollection
执行搜索的方式有关。对于 Spark,它在视图引擎的第一次迭代中“错过”,但在第二次迭代中“命中”,之后视图被添加到缓存中。没问题。
VirtualPathProviderViewEngine
,它在内部“未命中”,但在第一次迭代时无论如何都会返回“命中”,此时视图现在已被缓存。所以你应该能够看出问题出在哪里。
VirtualPathProviderViewEngine
仅似乎优先于SparkViewEngine
,因为前者总是在第一个上成功(缓存)迭代,但 Spark 仅在第二次(未缓存)迭代上成功。用简单的英语来说,Spark 确实首先被询问,但回答:“不,我还没有那个视图。尝试不使用缓存。”WebForms 得到第二个问,但自动说“我没有有那种观点,但我还是去给你做了一个,就在这里。”。从那时起,
WebFormViewEngine
始终获得优先级,因为它缓存了视图,而 Spark 没有。摘要:Spark 正在获得优先权,但由于 Spark 处理
useCache
参数的方式有一个怪癖,当 Web 表单引擎在以下位置处于活动状态时,它会被抛在后面:同一时间。要么 WebForm 过于渴望,要么 Spark 懒惰,这取决于您的观点。简而言之,解决方案是不要有冲突的视图!如果您注册了多个视图引擎,那么您应该将它们中的一个/两个都可以处理的任何视图名称视为未定义的行为。
The order in which you register the view engines doesn't matter (much). Rather, view engines take a set of
ViewLocationFormats
, and if a particular view path fits the formatted name, that engine will be used. Only if you have conflicting formats does the registration order matter.In the case of spark, views should have the
.spark
extension.WebFormViewEngine
will respond to any with.aspx
or.ascx
extensions. And of course, as mentioned above, you can override any of this by changing theViewLocationFormats
supplied to the individual view engines.Updated:
I took a look through the source of both
SparkViewFactory
andWebFormViewEngine
(or more specifically,VirtualPathProviderViewEngine
, which the latter derives from), and I can tell you why you're seeing this strange behaviour.First of all, the
Find
method in theViewEngineCollection
class works like this (simplified):In other words, it will always try to find a cached view, in any engine, before resorting to uncached mode.
The way in which individual view engines implement this is the second overload of the
FindView
method, which takes abool
argument nameduseCache
.However, and here's where it all gets weird - the
VirtualPathProviderViewEngine
andSparkViewEngine
have very different ideas of what theuseCache
argument means. There's too much code to repost here but the basic idea is:The
SparkViewFactory
will look only in the cache ifuseCache
istrue
. If it doesn't find anything, it automatically returns a "cache miss result" - i.e. nothing. On the other hand, ifuseCache
isfalse
, it will not look in the cache at all, it will skip the cache-checking step and go through the normal motions to resolve and create an actual view.The
VirtualPathProviderViewEngine
, on the other hand, looks in the cache ifuseCache
istrue
, and if it doesn't find the view in the cache, it goes off and creates a new one and adds that to the cache.Both of these approaches work with respect to the way
ViewEngineCollection
performs its search.In the case of spark, it "misses" on the first iteration of view engines, but "hits" on the second, and after that the view is added to the cache. No problem.
In the case of
VirtualPathProviderViewEngine
, it "misses" internally but returns a "hit" anyway on the first iteration, at which point the view is now cached.So you should be able to see where the problem is here. The
VirtualPathProviderViewEngine
only appears to be taking precedence over theSparkViewEngine
because the former always succeeds on the first (cached) iteration, but Spark only succeeds on the second (uncached) iteration.In plain English, Spark really does get asked first, but replies: "No, I don't have that view yet. Try it without the cache instead." WebForms gets asked second, but automatically says "I didn't have that view, but I went and made one for you anyway, here it is.". And from that point on, the
WebFormViewEngine
always gets priority because it has the view cached and Spark doesn't.Summary: Spark is getting priority, but due to a quirk in the way Spark treats the
useCache
argument, it's getting left in the dust when the Web Form engine is active at the same time. Either WebForm is over-eager or Spark is lazy, depending on your perspective.Simply put, the solution is not to have conflicting views! If you've registered multiple view engines, then you should be treating any view name which can be handled by either/both of them as undefined behaviour.
嗯...不 - 当 useCache 为 true 时,所有应有的尊重 webforms 不会做任何超出缓存检查的事情。与斯帕克相同。
实际上 - 我认为有人可能动了我的奶酪... Spark 可能添加了一个怪癖,导致 useCache==true 传递期间出现错误的缓存未命中。如果这是真的,那么它更像是一个错误,而不是应用于该参数的不同规则。
更新:
我最初正在研究 MVC 2 - 这就是为什么我暗示 @Aaronaught 的结论是不正确的。 MVC 2 不会在 useCache==true 的第一遍中返回视图,这与将解析和填充的 MVC 1.0 不同。
因此,ASP.NET MVC 1.0 和 ASP.NET MVC 2 的实现方式之间存在差异。 Spark 和 MVC 2 对 useCache 标志的处理方式相同,并且它们的注册顺序将赋予它们优先级。
Hmmm... Nope - all due respect webforms doesn't do anything beyond a cache-check when useCache is true. Same as Spark.
Actually - I think someone might have moved my cheese... Spark might have had a quirk added causing a false cache-miss during the useCache==true pass. If that's true it's more of a bug than different rules applied to that parameter.
Updated:
I was looking at MVC 2 originally - which is why I implied @Aaronaught's conclusions were incorrect. MVC 2 does not return a view on the first pass where useCache==true, which is different in MVC 1.0 which will resolve and populate.
So difference is between the way ASP.NET MVC 1.0 and ASP.NET MVC 2 are implemented. Spark and MVC 2 treat the useCache flag the same, and the order they are registered will give them priority.