In the last release of the BizTalk Deployment Framework, an additional feature was
added that I thought deserved its own post for explanation…

If you are responsible for troubleshooting BizTalk applications in production, you
are likely hungry for all the information you can get about why something is failing. 
Since all BizTalk project assemblies are in the GAC, the stack traces you
get (either from your own logging, or the event logs BizTalk generates for unhandled
exceptions) do not contain file and line number information.  This makes post-mortem
analysis a lot harder…

A new custom NAnt task (getgacpath) and corresponding support within BizTalkDeploymentInclude.nant
was added for placing PDB (program database) files for all BizTalk and component assemblies
into the GAC.  (You can use just this NAnt task without the rest of the Deployment
Framework, of course.  In addition, a standalone command line utility called
GetGacPath has been included in the “Tools” download.)

Why is this interesting?  Well, because it allows you to get stack traces that
include file and line number information even when assemblies reside in the GAC. 
(This is obviously useful and useable outside the domain of BizTalk…)  Without
extra work, file and line number information is generally missing from a stack
trace if a PDB file is not co-located physically with the corresponding assembly. 
Putting the PDB file directly into the GAC is an easy way of solving this problem.

Set the “deployPDBsToGac” NAnt property to “true” in your project-specific NAnt file
to get this functionality.

A couple of points are worth mentioning:

  • If the directory structure of the GAC were to ever change, this would need to be updated. 
    (See my upcoming post on using this technique with .NET 2.0.)

  • It would be great if Gacutil.exe provided this functionaity, but it doesn’t. 

  • You should strongly consider having your release-mode binaries (aka ‘deployment’ configuration
    in BizTalk) build PDB files – there is no reason not to.  Look under Project
    properties/Configuration properties/Build/Generate Debugging Information.

  • Stack traces that show up in the event log either through your own logging, or via
    BizTalk (because the exception was unhandled) will now have file and line number info. 
    For orchestrations, the file name will correspond to the temporary C# file generated
    at compile time.  If you use the techniques described in this
    post , you can correlate back to the actual statement that failed.  For instance,
    the event log might say:

    [4740] ERROR BizTalkSample.Orchestrations.TopLevelOrch - An exception was caught. 
    System.Exception: Something exceptional happened.
       at BizTalkSample.Orchestrations.TopLevelOrch.segment2(StopConditions stopOn) in 
       c:\Documents and Settings\Scott\Local Settings\Temp\h0li3wbi.0.cs:line 2538
  • If you grab the file mentioned (h0li3wbi.0.cs) using BTSFileDump (again,see here)
    you can cross-reference this message to a location in the generated C# code for TopLevelOrch:

    You might save off “h0li3wbi.0.cs” (the temp file name chosen when TopLevelOrch compiled) and
    the other generated files when you do your build, using BTSFileDump. (That would be
    tough to do if you build with CruiseControl, etc.) But you wouldn’t have to
    do that – if you can retrieve the source code used for a particular deployment,
    then the line number alone should be sufficient to get you to the exception
    site (since you can find the correct temp file by looking for comments, etc. that
    are in the orchestration – just like if you were using this
    technique to do live debugging.)  This all gets better with BizTalk 2006
    – see a future post on this topic. 

  • Final point: If you set deployPDBsToGac to “true”, deployments will begin by stopping
    the BizTalk services (instead of just bouncing the services at the end.)  The
    reason for this is that the BTSNTSvc.exe process will not “let go” of an assembly
    for which it has loaded a PDB file (so you have to terminate it.)  This means
    you may not always want this switch on for the developer edit/run/debug cycle.