Infosys Microsoft Alliance and Solutions blog

« Inside Vista Kernel | Main | LINQ and its linkage to new features in .NET Framework 3.5 - Part II,,, »

Loading Multiple Versions of same Assembly

The other day a colleague asked if there was a way he could work with different versions of the same assembly in his code i.e. could he invoke say Method A on Version 1.0 and Method B on Version 2.0 of the same assembly?

I was initially surprised as to why he would want to do that? People ask about being able to automatically load newer version of the assembly with the application or in case of .net being able to load an assembly built with .net framework 1.1 in an application built with .net framework 2.0, but usually not this.

When he explained his situation it made sense. He was working with Sharepoint Server API and there have been some changes in the API between SPS 2003 and 2007. So there was a need to invoke some functionality using SPS 2003 assembly and some functionality using the newer version available with SPS 2007.

I can think of another case where you have programmed against say a buggy assembly and have written some workaround logic for the bug in your code. With the newer version of that assembly that bug has been fixed, but due to some constraints, you can't remove your workaround code and hence can't use the new version. There is however new functionality provided by this new version which you do want to use. Hence you would need to be able to call new methods on new version and the buggy method on the old version.

With this preamble, lets get into how to get this done. There are various permutations and combinations and I will address them. At a high level the solution is in working with one version as already programmed (typically using early binding) and with another version using reflection. Do also note that the assembly loading follows probing rules as defined in .NET SDK documentation. To keep things simple, I will keep the folder path simple and not get into such diversions.

Consider this extremely critical business logic inside of a uniquely named assembly - ClassLibrary1.dll as below. Again to keep things simple, I am not showing the various "using" statements with the code. Additionally to identify which version the method is called on, I return the version# in the string itself. I can do a dynamic query for assembly version and remove this hard coding, but the recall.. our moto is simplicity at this time and focus on loading different versions and not any other aspect, hence the hard coding.

    public class Class1
    {
        public static string Method1()
        {
            return "Method 1 called on version 1.0.0.0";
        }
    }

Then there is this Winform application that invokes this static Method1 and displays the returned string in message box. To help identify if this call has happened due to early binding or reflection, I prefix the string accordingly. I build the code with version 1.0.0.0 of ClassLibrary1.dll and run the application. On invoking the method, I see a message box displaying the appropriate version#. Before we get into multiple versions, a quick discussion of what happens with private as against strong named assemblies is in order.

Weakly named library assembly (privately deployed)

If you update the version of such an assembly and redeploy it with the application, the application is able to load it without any issues. The change in just minor version to 1.1.0.0 or major version also to 2.0.0.0 doens't impact and the application works fine. Note that this is in line with basics of versioning that aren't applicable to weakly named assemblies.

Strongly named library assembly (privately deployed)

If you update the version of such an assembly and redeploy it with the applicaton, the application will give a runtime error when it tries to invoke a method of the library assembly. Due to strong naming, the version policy comes into effect and the application tries to invoked method on version against which it was complied, in this case 1.0.0.0. When it doens't finds this at runtime, it will give an error. It won't automatically start using 1.1.0.0 version. For that you need to specify the
bindingRedirect in the application configuration file.

There are multiple ways to create this configuration file. One is to do this with Visual studio itself and when the application is compiled, the config file is also complied and deployed along wih it. However if you aren't sure of the exact syntax, you can use the Microsoft .NET Framework 2.0 Configuration MMC from Administrators group. Via this you can add the specific application for configuration, then configure the specific library assembly and specify the binding policy. The configuration file generated will look something like this

<?xml version="1.0"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="ClassLibrary1" publicKeyToken="fbc28d9ca2fc8db5" />
        <publisherPolicy apply="no" />
        <bindingRedirect oldVersion="1.0.0.0" newVersion="1.1.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>


To reiterate, this bindingRedirect is required only for strongly named assemblies. Once this configuration setting is in place, the application is able to load the 1.1.0.0 assembly and invoke methods on it. The same logic will work for assemblies deployed in GAC.

Let us now get on with loading of different versions of the same assembly. See the sample code below on how to achieve it.

        private void btn_Click(object sender, EventArgs e)
        {
            string str = "early bind - ";
            str += ClassLibrary1.Class1.Method1();
            MessageBox.Show(str);
 
            Assembly al = Assembly.LoadFile(@"E:\Temp\old\ClassLibrary1.dll");
 
            Type t = al.GetType("ClassLibrary1.Class1");
            MethodInfo m = t.GetMethod("Method1");
            str = "reflection - " + (string)m.Invoke(null, null);
            MessageBox.Show(str);
        }

When we run the application, we expect the early binding call to go to the newer version and the call via reflection to go to the older version, that we have specifically deployed in another sub-folder. We are using Assembly.LoadFile method so that we can specify a path.

One important aspect to note here is that the application will have to be compiled with the newer version of the assembly if the assembly is strongly named. If that isn't done, as discussed earlier, we will have to use a bindingRedirect configuration setting. Such setting works for all assembly load calls and will even redirect the assembly load call that we are doing via reflection and hence we will not be able to invoke methods on older version. By explicitly building the application with newer version, we don't need to the bindingRedirect configuration setting and our reflection call to earlier version will then work.

Loading assemblies from GAC

There is slight difference in the way the assembly is loaded via reflection if working against GAC. To successfully load different versions from GAC it is best to use the AssemblyName class and specify the FullName of the assembly you want to load. As we all know, FullName includes name, version number, culture and public key token.

        private void btn_Click(object sender, EventArgs e)

        {

            string str = "early bind - ";

            str += ClassLibrary1.Class1.Method1();

            MessageBox.Show(str);

 

            AssemblyName asm = new AssemblyName("ClassLibrary1, Version=1.1.0.0, Culture=neutral, 
                                                                                            PublicKeyToken=fbc28d9ca2fc8db5"
);

            Assembly al = Assembly.Load(asm);

 

            Type t = al.GetType("ClassLibrary1.Class1");

            MethodInfo m = t.GetMethod("Method1");

            str = "reflection - " + (string)m.Invoke(null, null);

            MessageBox.Show(str);

        }

Needless to say that it also possible to load as many versions as you want using the option of reflection and you need not invoke any version via early binding. I have used to early binding option just to highlight a case where you are already working with a version and want to also specifically invoke methods on a different version. This also means that you can early bind to an older version and load the newer version by reflection.

While playing around with this, I also realized that one needs not worry about references added to the project. The compiler is intelligent enough to add them to the manifest only if a call is made to any method contained in the referenced assembly. If no call is made, the reference isn't included and hence the assembly won't be loaded at run time. You can easily verify this by viewing the manifest via ILDasm utility. 

 

 


 

Comments

One more way I can think of, to achieve the same would be using aliases on the dlls. What do you say?

Interesting point. However if you want to use alias for assemblies with same names, you can't easily work with Visual Studio and will have to rename the assembly else VS will not allow you to add references. You can go ahead with explicit command line build option.

However interestingly, if you refer http://msdn2.microsoft.com/en-us/library/ms379563(vs.80).aspx#csharpcompiler_topic9, it states - "This feature allows you to resolve name clashes between identically named types contained in uniquely named assemblies". In which case it means that this isn't really meant to be used in case assembly name is also same.

Finally, alias won't work with weakly named assemblies since CLR will not really know how to resolve reference to the right version.

Very good

Thank you
Its Ex'llent

Loading assembly with LoadFile() method is insane. First it hardcodes location, second it doesnt load the assemblies reffrenced by the assembly being loaded.

@Vipsy, what you say is right, but loading a version of the assembly not currently referrenced by the application is exactly what we are trying to achieve here. If you want to work with say version 1.0.0.0 and also versin 2.0.0.0 of the same assembly in an application, what other way can you think of without using LoadFile.

As is, assemblies for application are either deployed in private folder which is a standard path for the application and can be hardcoded (or we can derive using Application.StartupPath), OR they are deployed in GAC and then we can use the AssemblyName mechanism to load the right version.

This is a good solution actually. Thanks.

I have two different version of AjaxControlToolkit dll and I have added reference of both dll in my web application. I also set up different tagprefix for both the version.

Now one of the page is using 1.0 version and I want to use 3.0 version in another page. I am getting following error.

"The server tag 'ajaxToolkit:SliderExtender' is ambiguous. Please modify the associated registration that is causing ambiguity and pick a new tag prefix."

I read somewhere that we CANT use two different version of ajaxtoolkit dll on the same web application? I wanted to understand what is side by side assembly execution? Isn't it to run multiple version of the same DLL side by side? Why compiler doesn't know what dll I try to use If I have given different tagprefix for both DLL?

Shailesh, you don't need to use 2 different versions of AJAX assemblies. Only using the version 3.0 should be sufficient for your purpose as the 3.0 will anyway support features of 1.0 assembly.

Also note that Side by side is more for across application usage. So if app A is using version 1, and app B is using version 3, both version 1 and 3 can co-exist on the same machine.

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Please key in the two words you see in the box to validate your identity as an authentic user and reduce spam.

Subscribe to this blog's feed

Follow us on

Blogger Profiles

Infosys on Twitter