UsePhysicalViewsIfNewer Not Working?

May 22, 2013 at 11:56 AM
What does UsePhysicalViewsIfNewer do?

I downloaded the project at https://github.com/davidebbo/MvcApplicationRazorGeneratorSeparateLibrary to have a little play around. I assumed whenever I changed one of the views I wouldn't need to rebuild the project. However when I change the Home View and click refresh nothing happens until I do a rebuild.

I'd appreciate it if someone could clear this up for me. Thanks
Coordinator
May 22, 2013 at 5:39 PM
The project sets UsePhysicalViewsIfNewer only if running locally (see here. So it should find updated views at runtime only in the local case.
May 23, 2013 at 7:56 AM
Thanks David, that's what I had assumed. I am testing locally and still the contents of the page are not updated until I do a re-build.
Coordinator
May 23, 2013 at 1:43 PM
The way the flag works is it compares the last write times of the compiled assembly and the view files and determines which is newer when it needs to serve them. It works when your precompiled views are in the Mvc project being served. In the case of the sample above, the views are referenced from an externally compiled assembly and consequently the view engine has no way to figure out where the view files are.

It's definitely a bug in the behavior, but I'm not entirely certain how this scenario could be solved.
Coordinator
May 23, 2013 at 5:22 PM
Ah yes. Probably not much that can be done about that, since in this scenario, the aspx files essentially don't exist (from the point of you of IIS) when you run your site.
Coordinator
May 23, 2013 at 5:41 PM
We could add an extra property to the compiled view and point to the full path on disk. I'm not entirely sure that is do-able (being able to figure out the absolute path during compile time) and it's ugly to have the full path embedded in the generated type, but it might help in the local debugging scenario.
Coordinator
May 23, 2013 at 5:53 PM
If you're brave enough, you can get it from the PDB file of the compiled Views assembly.

However, I'm not even sure that the standard view engine will be able to process views that are outside the web root, short of using a VirtualPathProvider (which is painful).
May 24, 2013 at 10:17 AM
Ah thanks for letting me know. This perhaps should be documented somewhere as it's not immediately obvious. I can see the problem. However because the files do physically exist at design time, I may be able to customize the view engine to come up with something that suits my need.
May 24, 2013 at 2:33 PM
My attempt failed. So far I've stripped the view engine down to what I need:
protected override bool FileExists(ControllerContext controllerContext, string virtualPath) {
    return VirtualPathProvider.FileExists(virtualPath) || _mappings.ContainsKey(virtualPath);
}

protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) {
    Type type;

    // If the physical file exists then use that instead (useful for overriding), else try and find a virtual one
    if (VirtualPathProvider.FileExists(partialPath))
        return new RazorView(controllerContext, partialPath, null, false, FileExtensions);
    else if (_mappings.TryGetValue(partialPath, out type))
        return new PrecompiledView(type, partialPath, null, false, FileExtensions);

    return null;
}

protected override IView CreateView(ControllerContext controllerContext, string viewPath, string layoutPath) {
    Type type;

    // If the physical file exists then use that instead (useful for overriding), else try and find a virtual one
    if (VirtualPathProvider.FileExists(viewPath))
        return new RazorView(controllerContext, viewPath, layoutPath, true, FileExtensions);
    else if (_mappings.TryGetValue(viewPath, out type))
        return new PrecompiledView(type, viewPath, layoutPath, true, FileExtensions);

    return null;
}
I was able to remove all the IVirtualPathFactory stuff, i'm not sure if this is wise as I can't actually see what it's doing but it works.

I won't be using RazorGenerator for views within the web application project (only the referenced projects). Therefore if a view exists in the web application which has the same path as a view in a referenced project then it will use the web application one. I was hoping I could use this same code to return the one on disk if it's last modification date is more recent than the assembly last write time. However RazorView takes a virtual path and not a physical path. This could be the issue you mention above about the view existing outside of the web root. Is there an alternative way of doing this?