ASP.NET MVC 视图引擎解析顺序

发布于 2024-08-24 14:50:41 字数 823 浏览 6 评论 0原文

我创建了一个简单的 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

木格 2024-08-31 14:50:41

注册视图引擎的顺序并不重要。相反,视图引擎采用一组ViewLocationFormats,如果特定视图路径符合格式化名称,则将使用该引擎。仅当格式冲突时,注册顺序才重要。

对于 Spark,视图应具有 .spark 扩展名。 WebFormViewEngine 将响应任何具有 .aspx.ascx 扩展名的内容。当然,如上所述,您可以通过更改提供给各个视图引擎的 ViewLocationFormats 来覆盖其中的任何内容。


更新:

我查看了 SparkViewFactoryWebFormViewEngine(或更具体地说,VirtualPathProviderViewEngine)的源代码,其中后者源自),我可以告诉你为什么你会看到这种奇怪的行为。

首先,ViewEngineCollection 类中的 Find 方法的工作原理如下(简化版):

foreach (IViewEngine engine in Items) {
    // Query engine for cached view
}

foreach (IViewEngine engine in Items) {
    // Query engine for uncached view
}

换句话说,它将始终尝试在 中查找缓存的视图。任何引擎,然后再诉诸非缓存模式。

各个视图引擎实现此功能的方式是 FindView 方法的第二次重载,该方法采用名为 useCache 的 bool 参数。

但是,这就是一切变得奇怪的地方 - VirtualPathProviderViewEngineSparkViewEngineuseCache 的想法有非常不同的想法论证的意思。这里有太多代码需要重新发布,但基本思想是:

  • 如果 useCacheSparkViewFactory在缓存中查找>真实。如果它没有找到任何东西,它会自动返回一个“缓存未命中结果”——即什么也没有。另一方面,如果 useCachefalse,它根本不会在缓存中查找,它将跳过缓存检查步骤并通过正常的动作来解决并创建一个实际视图。

  • 另一方面,如果 useCachetrue,则 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 the ViewLocationFormats supplied to the individual view engines.


Updated:

I took a look through the source of both SparkViewFactory and WebFormViewEngine (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 the ViewEngineCollection class works like this (simplified):

foreach (IViewEngine engine in Items) {
    // Query engine for cached view
}

foreach (IViewEngine engine in Items) {
    // Query engine for uncached view
}

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 a bool argument named useCache.

However, and here's where it all gets weird - the VirtualPathProviderViewEngine and SparkViewEngine have very different ideas of what the useCache argument means. There's too much code to repost here but the basic idea is:

  • The SparkViewFactory will look only in the cache if useCache is true. If it doesn't find anything, it automatically returns a "cache miss result" - i.e. nothing. On the other hand, if useCache is false, 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 if useCache is true, 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 the SparkViewEngine 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.

半寸时光 2024-08-31 14:50:41

嗯...不 - 当 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文