Search code examples
.netweb-configclrgac

CLR Binds to the Correct Assembly with bindingRedirect, But How?


A little background: we have an app that uses 3 core DLLs that are referenced by different pieces of the application. These three DLLs are in the GAC. In our test environment we have 3 test websites (same website, different versions) that all use the DLLs in the GAC, as well as product specific DLLs in bin\.

We've run into a situation where one of the products required updates to these core DLLs, but we didn't want to place the assemblies in the GAC as our specific test website was the only one referencing these DLLs. We are aware that we can place multiple versions of an assembly in the GAC, but that's not related to this specific question because we have opted not to do this.

My own research showed a bunch of people who seemed very confident in their answers, but most of them differed re: the runtime locating assemblies. Most said the GAC takes precedence, which in my own experience has been the case. Others said as long as the assembly isn't strongly named it will prefer the bin\ over the GAC, which in my experience was incorrect.

I found this MS article:

How the Runtime Locates Assemblies

which describes the steps the CLR goes through to determine which assemblies to bind to. This proved to not behave exactly as expected. All of this may very well be an environmental issue (I would actually lean that way).

I also found this SO question that was similar to others I had read but not necessarily the answer I needed

I finally found a workaround that would prefer the bin\ over the GAC. I used the tag in our web.config. I had seen this suggested with intermittent success on SO and other sites and was pleased that it worked in my case.

<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <dependentAssembly>
            <assemblyIdentity name="dll1" publicKeyToken="ourToken" 
                culture="neutral" />
            <bindingRedirect oldVersion="5.0.0.0" newVersion="5.0.1.0" />
        </dependentAssembly>
    </assemblyBinding>
</runtime>

Version 5.0.1.0 is in my bin\ folder. 5.0.0.0 is in the GAC. Finally to my question, how does the CLR know, even though 5.0.1.0 is not an assembly in the GAC, that it needs to look in the bin\ to find the correct assembly to bind to? Before adding the tag in the .config, regardless of what assembly version the project was built with (5.0.1.0) the application was still preferring the older version DLL from the GAC (5.0.0.0).

I haven't been able to find any documentation on this, most likely because I don't know how to break down this 5 paragraph explanation into a search query. Any reference material would be appreciated, or maybe even clarification into the articles referenced above so I can get an understanding of why the CLR is doing what it's doing.


Solution

  • It just always look in the GAC first, searching for the specific name + version of the assembly. If it isn't found then it goes looking for the assembly in the probing path. And looks only for the specific name of the assembly. When it finds one then it checks if the version matches. Kaboom if it doesn't.

    So originally it always looked for "dll1" + 5.0.0.0 and found it in the GAC. No need to go looking elsewhere.

    After you changed the .config file, it now looks for "dll1" + 5.0.1.0 in the GAC. And doesn't find it there. So now goes looking for "dll1" and finds it in your bin directory. Version matches so it is happy.