Search code examples
asp.netdebuggingembedded-resourcevirtualpathprovider

Cannot debug EmbeddedResource views loaded via custom VirtualPathProvider


I have written a custom VirtualPathProvider (source here) which will return content from EmbeddedResources, or from the original file if it has been told where to find it (this allows you to edit and update the files without having to rebuild). This is working fine, so far.

What isn't working is debugging. If I add a breakpoint to the view, it doesn't load the symbols. I can see why this is difficult (how can the ASP compiler know where the source file is, in order to spot the breakpoints?), but am looking for a way to hint to the compiler where the source file can be found.

Example project here: http://dl.dropbox.com/u/2808109/VppDebugTest.zip

edit:

I've been experimenting with an ASPX page loaded via the VPP, and by viewing the Compiled Source (using David Ebbo's technique), and the line pragmas are generated like so:

Line 275:              #line 1 "http://server/EmbeddedPage.aspx"
Line 276:              this.InitializeCulture();

Normally, these are generated along the lines of

Line 275:              #line 1 "d:/somesln/someproj/EmbeddedPage.aspx"

Don't know if that helps anyone, or not...

edit 2:

After David sent me his code, I have done some further investigation and the following things seem to be true:

  1. you can't set a breakpoint in a .aspx unless system.web is referenced (in VS 2010)
  2. if you create a minimal .aspx page with the directives <%@ Page Language="C#" %> and set a breakpoint, VS will stop at the breakpoint in the source file

  3. if you create a non minimal .aspx with directives <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="VppDebugTest.WebForm1" %> and set a breakpoint, when viewed VS will take you to the dissasembly debug view

--- http://server/WebForm1.aspx ------------------------------------------------ 0000003a mov ecx,dword ptr [ebp-3Ch] 0000003d call 63EC54F0 00000042 mov dword ptr [ebp-44h],eax 00000045 mov edx,dword ptr ds:[03E62200h] 0000004b mov ecx,dword ptr [ebp-44h]

It still wont stop at any breakpoints in the Razor views, which is unfortunately what I really need to be able to do! This .aspx stuff may be a red herring.

edit:

5: If I put a call to Debugger.Break() into my Index.cshtml, the debugger stops at disassembly view, and there are no pragmas at all, incorrect or otherwise

  1. If I manually write @{ #line 1 "C:\Users\Harry\Desktop\VppDebugTest\VppDebugTest.Views\Views\Home\Index.cshtml" } in my view, the debugged will stop in the file. So maybe the solution is for my VPP to insert the #line pragmas into the cshtml files itself??

Solution

  • I had the same problem and finally got it working by using a custom RazorHost. It seems that the physical file location is resolved using the HostingEnvironment.MapPath() method which does not return the correct result for embedded files.

    What I did:

    public class MyCustomRazorHostFactory : WebRazorHostFactory
    {
        public override System.Web.WebPages.Razor.WebPageRazorHost CreateHost( string virtualPath, string physicalPath )
        {
            // Implementation stolen from MvcRazorHostFactory :)
            var host = base.CreateHost( virtualPath, physicalPath );
    
            if( !host.IsSpecialPage )
            {
                return new MyCustomRazorHost( virtualPath, physicalPath );
            }
    
            return host;
        }
    }
    
    public class MyCustomRazorHost : MvcWebPageRazorHost
    {
        public MyCustomRazorHost( string virtualPath, string physicalPath )
            : base( virtualPath, physicalPath )
        {
            if( MyMagicHelper.IsEmbeddedFile( virtualPath ) )
            {
                PhysicalPath = MyMagicHelper.GetPhysicalFilePath(virtualPath);
            }
        }
    }
    
    // Simplified for demonstration purpose
    public static class MyMagicHelper
    {
        public static bool IsEmbeddedFile(string virtualPath)
        {
            // ... check if the path is an embedded file path
        }
    
        public static string GetPhysicalFilePath(string virtualPath)
        {
            // ... resolve the virtual file and return the correct physical file path
        }
    }
    

    As a last step you need to tell ASP.NET which host factory it should use. This is done in the web.config:

    <system.web.webPages.razor>
        <host factoryType="My.Custom.Namespace.MyCustomRazorHostFactory" />
    </system.web.webPages.razor>
    

    I know my answer comes a bit late but hopefully someone else can make use of it when stumbling across this question as I did. :)