Cannot debug EmbeddedResource views loaded via custom VirtualPathProvider
Asked Answered
D

2

10

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??
Dannydannye answered 17/5, 2012 at 9:48 Comment(6)
In your attached project, what are the exact steps that lead to seeing the repro? e.g. what do I navigate to, when do I attach, where do I set the BP, etc...Phipps
Set a breakpoint inside index.cshtml (e.g. on the @ViewBag.Message line) and the debugger will not stop there if you run the project. Add a call to Debugger.Break() and you get disassembly, not the source code.Dannydannye
Hmmm, this is strange. The generated file in this case doesn't contain any pragmas at all! Note that the code generators for aspx and cshtml (aka Razor) pages are completely different, so this may be an issue specific to Razor.Phipps
Yes, I think I muddied the water by mentioning .aspxesDannydannye
I am having the exact same problem. For debugging a single issue inserting the @{ #line 1 "..." } is an OK workaround. But I was wondering if you ever found a real solution to this?Hilariohilarious
No I never got to the bottom of it :(Dannydannye
H
2

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. :)

Hereafter answered 28/2, 2015 at 9:54 Comment(2)
wow great, i'll see if I can integrate this into my nuget package when i get a chanceDannydannye
Finally got round to doing it and it works!!! I've exposed some properties on my EmbeddedResourceVirtualPathProvider project to implement the methods, gist at gist.github.com/mcintyre321/ff67ffa8e2f0c8ef86daDannydannye
P
1

I tried playing with your code a bit, and when I added a test aspx in the resources, debugging appeared to work fine. I was able to set a BP in Page_Load, and it git there.

You can see my change in https://github.com/davidebbo/EmbeddedResourceVirtualPathProvider

Note that I disabled the fallback logic as I wanted to focus on the embedded case, though I don't think that makes a difference.

Note that I'm using VS2012, so I also had to upgrade the project/sln (but they'll still work in 2010).

The reason ASP.NET is generating the http line pragma is that it can't find the physical aspx file at the standard location (i.e. what MapPath would return). There is actually a little known way to always turn on this behavior: set urlLinePragmas=true in the section (http://msdn.microsoft.com/en-us/library/system.web.configuration.compilationsection.urllinepragmas.aspx).

Phipps answered 5/9, 2012 at 21:56 Comment(2)
Thanks for looking into this! You've raised a few interesting things, I'll update the question to reflect them.Dannydannye
Is there some way I can hook into the lookup ASP is doing for the urlLinePragmas and tell it where the real source file is? (Would this fix my problem with Razor views)Dannydannye

© 2022 - 2024 — McMap. All rights reserved.