There's a major bug in the VirtualPathProviderViewEngine that only shows up when running in production. When you run with <compilation debug="false" /> in your web.config, the VirtualPathProviderViewEngine will attempt to cache the location of views using
the AspNetCacheLocationCache.
Unfortunately, the cache key that it uses is not unique across controller and view - instead it just uses the view nameas the cache key.
So, if you have two controller actions with the same name (eg an Index action on a HomeController and an Index action on a Customers controller) and you visit mysite.com/home/index, then mysite.com/customers/index, then the Index view for HomeController
will be rendered instead.
The problem is on line 144 of VirtualPathProviderViewEngine.cs
The following test can be used to reproduce the issue (I added this to WebFormViewEngineTest.cs in order to get access to the internal properties of the view engine):
[TestMethod]
public void ShouldCacheCorrectly() {
var viewEngine = new TestableViewEngine();
viewEngine.ViewLocationCache = new TestCache();
var mockContext = new Mock<HttpContextBase>();
var controller = new Mock<ControllerBase>();
var routeData = new RouteData();
routeData.Values.Add("controller", "home");
routeData.Values.Add("action", "index");
var context = new ControllerContext(mockContext.Object, routeData, controller.Object);
var firstView = (MockView)viewEngine.FindView(context, "index", null).View;
routeData = new RouteData();
routeData.Values.Add("controller", "customers");
routeData.Values.Add("action", "index");
var secondContext = new ControllerContext(mockContext.Object, routeData, controller.Object);
var secondView = (MockView)viewEngine.FindView(secondContext, "index", null).View;
Assert.AreNotEqual(firstView.Path, secondView.Path);
}
private class TestableViewEngine : WebFormViewEngine {
public TestableViewEngine() {
VirtualPathProvider = new TestPathProvider();
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) {
return new MockView() { Path = viewPath };
}
}
private class MockView : IView {
public string Path { get; set; }
public void Render(ViewContext viewContext, TextWriter writer) {
}
}
private class TestCache : IViewLocationCache {
private Hashtable cache = new Hashtable();
public string Get(string cacheKey) {
return (string)cache[cacheKey];
}
public void Set(string cacheKey, string virtualPath) {
cache[cacheKey] = virtualPath;
}
}
public class TestPathProvider : VirtualPathProvider {
public override bool FileExists(string virtualPath) {
return true;
}
}
thank you thank you thank you! you saved me big time with this.
even though it might be fixed in the current release, if you have anything else that distinguishes pages apart (like a cookie or a session variable - used in theming) then you'll still run into this problem. which i did. so i added my themeing value on to
this and it works. big time life saver!
JeremyS
Member
506 Points
99 Posts
Major bug in VirtualPathProviderViewEngine
Sep 06, 2008 01:47 PM|LINK
There's a major bug in the VirtualPathProviderViewEngine that only shows up when running in production. When you run with <compilation debug="false" /> in your web.config, the VirtualPathProviderViewEngine will attempt to cache the location of views using the AspNetCacheLocationCache.
Unfortunately, the cache key that it uses is not unique across controller and view - instead it just uses the view nameas the cache key.
So, if you have two controller actions with the same name (eg an Index action on a HomeController and an Index action on a Customers controller) and you visit mysite.com/home/index, then mysite.com/customers/index, then the Index view for HomeController will be rendered instead.
The problem is on line 144 of VirtualPathProviderViewEngine.cs
Change this:
string cacheKey = cacheKeyPrefix + name;
to:
string cacheKey = cacheKeyPrefix + controller + name;
JeremyS
Member
506 Points
99 Posts
Re: Major bug in VirtualPathProviderViewEngine
Sep 06, 2008 03:16 PM|LINK
The following test can be used to reproduce the issue (I added this to WebFormViewEngineTest.cs in order to get access to the internal properties of the view engine):
[TestMethod] public void ShouldCacheCorrectly() { var viewEngine = new TestableViewEngine(); viewEngine.ViewLocationCache = new TestCache(); var mockContext = new Mock<HttpContextBase>(); var controller = new Mock<ControllerBase>(); var routeData = new RouteData(); routeData.Values.Add("controller", "home"); routeData.Values.Add("action", "index"); var context = new ControllerContext(mockContext.Object, routeData, controller.Object); var firstView = (MockView)viewEngine.FindView(context, "index", null).View; routeData = new RouteData(); routeData.Values.Add("controller", "customers"); routeData.Values.Add("action", "index"); var secondContext = new ControllerContext(mockContext.Object, routeData, controller.Object); var secondView = (MockView)viewEngine.FindView(secondContext, "index", null).View; Assert.AreNotEqual(firstView.Path, secondView.Path); } private class TestableViewEngine : WebFormViewEngine { public TestableViewEngine() { VirtualPathProvider = new TestPathProvider(); } protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { return new MockView() { Path = viewPath }; } } private class MockView : IView { public string Path { get; set; } public void Render(ViewContext viewContext, TextWriter writer) { } } private class TestCache : IViewLocationCache { private Hashtable cache = new Hashtable(); public string Get(string cacheKey) { return (string)cache[cacheKey]; } public void Set(string cacheKey, string virtualPath) { cache[cacheKey] = virtualPath; } } public class TestPathProvider : VirtualPathProvider { public override bool FileExists(string virtualPath) { return true; } }Eilon
Contributor
5753 Points
976 Posts
Microsoft
Re: Major bug in VirtualPathProviderViewEngine
Sep 08, 2008 04:54 AM|LINK
Thanks for the bug report! We just noticed this bug ourselves and we'll have the bug fixed in the next release of ASP.NET MVC.
For now I believe that one workaround is to use an explicit relative path when using the View API:
return View("~/Views/MyController/MyView.aspx");
And of course another workaround is to just download the source code and do the temporary fix.
Thanks again,
Eilon
ericsims
Member
2 Points
1 Post
Re: Major bug in VirtualPathProviderViewEngine
Jun 09, 2010 09:43 PM|LINK
thank you thank you thank you! you saved me big time with this.
even though it might be fixed in the current release, if you have anything else that distinguishes pages apart (like a cookie or a session variable - used in theming) then you'll still run into this problem. which i did. so i added my themeing value on to this and it works. big time life saver!