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.