TFS 2013 plugins cannot load dependencies located in the "Plugins" folder

I have a plugin for TFS 2010 that reads a config file containing custom section handlers. When I deployed my plugin on TFS 2013, it thrown the following error when trying to instantiate those section handlers :

A first chance exception of type 'System.Configuration.ConfigurationErrorsException' occurred in System.Configuration.dll

Additional information: An error occurred creating the configuration section handler for xxxxx: Could not load file or assembly 'xxxxx' or one of its dependencies. The system cannot find the file specified.

The problem was that there was no adequate probing path defined for .NET 4.5 in the TFS 2013 Services' web config.

Click to Read More

TFS 2013 Services are loading plugins from C:\Program Files\Microsoft Team Foundation Server 12.0\Application Tier\Web Services\bin\Plugins.

But once those plugins loaded, they can only load their own dependencies from the GAC or from the TFS 2013 Services' bin folder (C:\Program Files\Microsoft Team Foundation Server 12.0\Application Tier\Web Services\bin). I.e.: the sub-folder 'Plugins' of that bin folder is not probed...

In my case, when my plugin read its custom section, it needs to load the assembly containing the related custom section handlers (actually, the plugin and the custom handlers are in the very same assembly...). It works fine if I deploy the assembly in TFS 2013 Services' bin folder (or in the GAC I presume) but I cannot bring myself  to simply do that. Especially taking into account that it was working fine within TFS 2010.

Having a look at TFS 2013 Services' web config, in order to specify my own probing path, I noticed that there was already one defined, but ONLY for .Net 2.0. I.e.: here is the <runtime> section of C:\Program Files\Microsoft Team Foundation Server 12.0\Application Tier\Web Services\web.config

<!-- Plugin directory path -->
<runtime>
<assemblyBinding appliesTo="v2.0.50727" xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="bin\Plugins;bin\Plugins\zh-chs;bin\Plugins\zh-cht;bin\Plugins\de;bin\Plugins\es;bin\Plugins\fr;bin\Plugins\it;bin\Plugins\ja;bin\Plugins\ko"/>
<dependentAssembly>
<assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>

As my plugin targets .Net 4.5, I simply defined the probing path for all target runtime.

<!-- Plugin directory path -->
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="bin\Plugins;bin\Plugins\zh-chs;bin\Plugins\zh-cht;bin\Plugins\de;bin\Plugins\es;bin\Plugins\fr;bin\Plugins\it;bin\Plugins\ja;bin\Plugins\ko"/>
</assemblyBinding>

<assemblyBinding appliesTo="v2.0.50727" xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>

And that solved my issue !

FYI (and google indexing): my plugin is a Server Side Event Handler for TFS...

Leave a Reply

Your email address will not be published. Required fields are marked *