Jon Flanders' Blog

Warning - putting pipeline components into the GAC dangerous for you mental health

Saturday, February 12, 2005 8:00:00 AM (GMT Standard Time, UTC+00:00)

One of the things you get used to developing with BizTalk is putting assemblies in the GAC. It becomes almost second nature to sign all the assemblies you create for use with BizTalk and run gacutil -i on them.

But as soon as you start building custom pipeline classes and adapters you must resist the urge to put assemblies in the GAC. Assemblies containing custom pipeline classes need to go into the "Microsoft BizTalk Server 2004\Pipeline Components\" directory (generally C:\Program Files\Microsoft BizTalk Server 2004\Pipeline Components") instead of into the GAC. The only reason I have ascertained for this requirement relates to the Pipeline Designer in Visual Studio .NET. For a type to appear on the Toolbox in VS.NET, the assembly containing that type has to be in that folder. When you right-click on the Toobox and select "Add/Remove Items" - the Pipeline Components tab reads directly from that folder. It loads all the assemblies in that special directory and uses reflection to find all the classes in those assemblies that have a certain custom attribute value associated with that type (the ComponentCategory attribute initialized to the CategoryTypes.CATID_PipelineComponent value. Nothing really wrong with this design.

The problem of course comes during assembly loading time. When .NET needs a type that isn't already in a loaded assembly, it uses a very specific set of rules to find and load the needed assembly. See How the Runtime Locates Assemblies. When an assembly is loaded, that assembly is given an associated binding context (for more info on this concept see here and here. The binding context relates to where the assembly was loaded from, and can be used by the assembly loading infrastructure about where to load dependent assemblies from. Most often this comes into play not with assemblies loaded on demand, but with assemblies which are explicitly loaded via calls to Assembly.LoadFrom. When you call Assembly.LoadFrom specifying the exact CodeBase from which to load the assembly, that CodeBase is used as a hint (stored in the binding context) by the Assembly Resolver when looking for dependant assemblies of the assembly loaded with LoadFrom. As an example, if you had an assembly (let's call it asm.dll) that you loaded with a call like this:

Assembly a;
a = Assembly.LoadFrom(@"C:\bin\asm.dll");

Once asm.dll is loaded, any assemblies that asm.dll references can be loaded from "C:\bin" instead of using the standard assembly loading rules. One issue that can arise when someone starts using Assembly.LoadFrom, is that when an assembly gets loaded with a specific binding context, it isn't considered the "same" assembly as an assembly with the exact same fully-qualified assembly name. In my example if asm.dll also got loaded from the GAC, the CLR would consider those two assemblies as being "different" (even if they are really exactly the same). If asm.dll contained a Type named "Foo",calling Type.Equals on an instances of Foo loaded from the asm.dll loaded via Assembly.LoadFrom, passing in the Type of an instanced loaded from the asm.dll from the GAC dynamically would always return false.

The upshot of this for BizTalk is that if an assembly containing a pipeline component is loaded from the correct directory (the Pipeline Components directory under the BizTalk install directory) and loaded from the GAC, things could easily start to break.

When a pipeline is being configured, the messaging infrastructure in BizTalk calls Assembly.LoadFrom on all the types configured inside of the pipeline configuration (this is the XML document that a type that derives from BasePipeline holds in it's XmlContent property). The calling code is actually unmanaged COM code, and the ComponentLoader type inside of Microsoft.BizTalk.Messaging.dll calls Assembly.LoadFrom on all pipeline assemblies.

The morale of this whole discussion is that you absolutely, positively, under no circumstances should put an assembly containing a pipeline component into the GAC. No matter what odd assembly loading errors you might be getting, you have to solve the problem another way. What bad things could happen? Well, it is a similar problem calling Windows Forms Controls from a thread other than the thread that created them, it will not always be clear immediately what the problems you might have are, but they will come, and they will be difficult to debug.

BizTalk   #    Comments [6]   Tracked by:
"How Pipeline Component Assemblies are Loaded" (Commonality) [Trackback]
http://www.winterdom.com/weblog/2006/10/06/HowPipelineComponentAssembliesAreLoad... [Pingback]

Tuesday, February 15, 2005 11:14:54 PM (GMT Standard Time, UTC+00:00)
Awesome post. Do you know if anyone has plans for a BizTalk 2004 cookbook?
Like the ASP.NET Cookbook: http://aspalliance.com/cookbook/default.aspx


Monday, April 10, 2006 3:55:32 PM (GMT Daylight Time, UTC+01:00)
And what if you need multiple versions of the same pipeline ???
Monday, April 10, 2006 3:57:07 PM (GMT Daylight Time, UTC+01:00)
I'm talking about Pipeline components - not pipelines themselves. If you need mulitple versions of particular pipeline component implementation - you need to put them in different assemblies (assemblies with different names).
Tuesday, April 11, 2006 9:17:53 AM (GMT Daylight Time, UTC+01:00)
In the end this is what I will do. But this completly ruins the concept of GAC and having multiple versions of the assembly! Anyway, thanks for the tip!
Tuesday, January 08, 2008 10:32:59 PM (GMT Standard Time, UTC+00:00)
This is NOT a good reason to refrain from GACing Pipeline Components. When you are creating a pipeline you can see where the pipeline components will be loaded from by looking at the properties. If the path property is "C:\windows\assembly\...." then it is loading from the GAC. If it is "<BizTalk Directory>\Pipeline Components" then it is not being loaded from the GAC.

You are actually better off never adding assemblies to the BizTalk\Pipeline Components directory. That will also help you avoid the problem you refer to above. Then, all Pipeline Components must be loaded from the GAC. You may find that you want to place PC in the BizTalk\PC directory on development machines so that they automatically appear in the toolbox items. That is fine as long as you make sure that you have GACed the assembly before you use it in the designer. Just remember to look at the path property to see where it is loading from. If it is loading from the BizTalk\PC directory then you will get a FNF error when you go to production (since the PCs are only GACed there).

On a last note, you don't have to put the PC in the BizTalk\PC directory to use it from the ToolBox. Just right click on the Toolbox, click "choose items", click the pipelines tab (back right), and click the browse button. Find your GACed assembly and hit OK. Check the boxes next to the PC that you want and hit OK.
Tuesday, January 08, 2008 10:34:44 PM (GMT Standard Time, UTC+00:00)
Scott - this was a post about BizTalk Server 2004 - BizTalk Server 2006 and .NET 2.0 changed the rules - since if an assembly is found in the GAC - it will be used regardless of if it was asked to be loaded with Assembly.LoadFrom or Assembly.Load
All comments require the approval of the site owner before being displayed.
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview