This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.

Title

Configuring .NET Framework Assembly-binding Redirects

Description

After years of getting incrementally better at fixing binding redirects, I've finally taken the time to document my methodology for figuring out what to put into <c>app.config</c> or <c>web.config</c> files. The method described below works: when you get an exception because the runtime gets an unexpected version of an assembly---e.g. <iq>The located assembly’s manifest definition does not match the assembly reference</iq>---this technique lets you formulate a binding-redirect that will fix it. You'll then move on to the <i>next</i> binding issue, until you've taken care of them all and your code runs again. <h>Automatic Binding Redirects</h> If you have an executable, you can usually get Visual Studio (or MSBuild) to regenerate your binding redirects for you. Just delete them all out of the <c>app.config</c> or <c>web.config</c> and <i>Rebuild All</i>. You should see a warning appear that you can double-click to generate binding redirects. If, however, this doesn't work, then you're on your own for discovering which version you actually have in your application. You need to know the version or you can't write the redirect. You can't just take any number: it has to match exactly. <h>Testing Assemblies</h> Where the automatic generation of binding redirects <i>doesn't</i> work is for unit-test assemblies. My most recent experience was when I upgraded <c>Quino-Windows</c> to use the latest <c>Quino-Standard</c>. The <c>Quino-Windows</c> test assemblies were suddenly no longer able to load the PostgreSql driver. The <c>Quino.Data.PostgreSql</c> assembly targets .NET Standard 2.0. The testing assemblies in <c>Quino-Windows</c> target .NET Framework. After the latest upgrade, many tests failed with the following error message: <error>Could not load file or assembly 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)</error> This is the version that it was <i>looking for</i>. It will either be the version required by the loading assembly (<c>npgsql</c> in this case) or the version already specified in the <c>app.config</c> (that is almost certainly out of date). <h>Which File Was Loaded?</h> To find out the file version that your application <i>actually uses</i>, you have to figure out which assembly .NET loaded. A good first place to look is in the output folder for your executable assembly (the testing assembly in this case). If, for whatever reason, you can't find the assembly in the output folder---or it's not clear which file is being loaded---you can tease the information out of the exception itself. <ol> From the exception settings, make sure that the debugger will stop on a <c>System.IO.FileLoadException</c> Debug your test The debugger should break on the exception </ol> <img src="{att_link}assemblybindingredirect_exception.png" align="none" caption="Assembly-binding exception"> Click "View Details" to show the <i>QuickWatch</i> window for the exception. There's a property called <c>FusionLog</c> that contains more information. <img src="{att_link}showing_details_in_the_fusionlog.png" href="{att_link}showing_details_in_the_fusionlog.png" align="none" caption="Fusion log details (with loaded assembly path)" scale="40%"> The log is quite detailed and shows you the configuration file that was used to calculate the redirect as well as the file that it loaded. <img src="{att_link}assemblybindingredirect_fusionlog.png" href="{att_link}assemblybindingredirect_fusionlog.png" align="none" caption="Assembly-binding 'fusion log'" scale="50%"> <h>Which Version Is It?</h> With the path to the assembly in hand, it's time to get the assembly version. Showing the file properties will most likely not show you the assembly version. For third-party assemblies (e.g. Quino), the file version is <i>often</i> the same as the assembly version (for pre-release versions, it's not). However, Microsoft <i>loves</i> to use a different file version than the assembly version. That means that you have to open the assembly in a tool that can dig that version out of the assembly manifest. The easiest way to get the version number is to use the free tool <i>JetBrains DotPeek</i> or use the <i>AssemblyExplorer</i> in <i>JetBrains ReSharper</i> or <i>JetBrains Rider</i>. You can see the three assemblies that I had to track down in the following screenshot. <img src="{att_link}actualversions.png" align="none" caption="Actual versions of various System assemblies"> <h>Writing Binding Redirects</h> Armed with the actual versions and the public key-tokens, I was ready to create the <c>app.config</c> file for my testing assembly. <img src="{att_link}assemblybindingredirect_mappings.png" href="{att_link}assemblybindingredirect_mappings.png" align="none" caption="Assembly-binding mappings" scale="75%"> And here it is in text/code form: <code> <configuration> <runtime> <assemblybinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentassembly> <assemblyidentity name="System.Numerics.Vectors" publicKeyToken="B03F5F7F11D50A3A" culture="neutral"> <bindingredirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0"> </dependentassembly> <dependentassembly> <assemblyidentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="B03F5F7F11D50A3A" culture="neutral"> <bindingredirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0"> </dependentassembly> <dependentassembly> <assemblyidentity name="System.Threading.Tasks.Extensions" publicKeyToken="CC7B13FFCD2DDD51" culture="neutral "> <bindingredirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1"> </dependentassembly> </assemblybinding> </runtime> </configuration> </code>