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.