A New Internet Library: Add Your Website/Blog or Suggest A Website/Blog to our Free Web Directory http://anil.myfunda.net.

Its very simple, free and SEO Friendly.
Submit Now....

Thursday, May 8, 2008

Loading Multiple Versions of same Assembly

In some cases 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. 

 

HTTP Status Codes that the Web Server can return

HTTP Status Codes that the Web Server can return - Definitions

  1. 200 OK
  2. 301 Moved Permanently
  3. 302 Found
  4. 304 Not Modified
  5. 307 Temporary Redirect
  6. 400 Bad Request
  7. 401 Unauthorized
  8. 403 Forbidden
  9. 404 Not Found
  10. 410 Gone
  11. 500 Internal Server Error
  12. 501 Not Implemented

HTTP Status Code - 200 OK

The request has succeeded. The information returned with the response is dependent on the method used in the request.

Back to top


HTTP Status Code - 301 Moved Permanently

The requested resource has been assigned a new permanent URI and any future references to this resource SHOULD use one of the returned URIs.

Test URI: /w3c/status-codes/301.asp

Back to top


HTTP Status Code - 302 Found

The requested resource resides temporarily under a different URI. Since the redirection might be altered on occasion, the client SHOULD continue to use the Request-URI for future requests.

Test URI: /w3c/status-codes/302.asp

Admin Note: 2005-03-14 - During the past several months, many discussions have been taking place in the search engine marketing communities concerning what many refer to as pagejacking. What you are about to read concerns everyone who has a website online, this is not an isolated incident. Those who are involved with the page jacking issues may not even know that they are causing harm to those they are linking to using a 302 Found redirect. Many of the 302s are generated from exit tracking scripts.

Back to top


HTTP Status Code - 304 Not Modified

If the client has performed a conditional GET request and access is allowed, but the document has not been modified, the server SHOULD respond with this status code. The 304 response MUST NOT contain a message-body, and thus is always terminated by the first empty line after the header fields.

Back to top


HTTP Status Code - 307 Temporary Redirect

The requested resource resides temporarily under a different URI. Since the redirection MAY be altered on occasion, the client SHOULD continue to use the Request-URI for future requests. This response is only cacheable if indicated by a Cache-Control or Expires header field.

Test URI: /w3c/status-codes/307.asp

Back to top


HTTP Status Code - 400 Bad Request

The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.

Back to top


HTTP Status Code - 401 Unauthorized

The request requires user authentication. The response MUST include a WWW-Authenticate header field containing a challenge applicable to the requested resource.

Back to top


HTTP Status Code - 403 Forbidden

The server understood the request, but is refusing to fulfill it. Authorization will not help and the request SHOULD NOT be repeated.

Back to top


HTTP Status Code - 404 Not Found

The server has not found anything matching the Request-URI. No indication is given of whether the condition is temporary or permanent.

Test URI: /w3c/status-codes/404.asp

Back to top


HTTP Status Code - 410 Gone

The requested resource is no longer available at the server and no forwarding address is known. This condition is expected to be considered permanent. Clients with link editing capabilities SHOULD delete references to the Request-URI after user approval.

If the server does not know, or has no facility to determine, whether or not the condition is permanent, the status code 404 Not Found SHOULD be used instead. This response is cacheable unless indicated otherwise.

Test URI: /w3c/status-codes/410.asp

Back to top


HTTP Status Code - 500 Internal Server Error

The server encountered an unexpected condition which prevented it from fulfilling the request.

Back to top


HTTP Status Code - 501 Not Implemented

The server does not support the functionality required to fulfill the request. This is the appropriate response when the server does not recognize the request method and is not capable of supporting it for any resource.

Back to top


W3C References

  1. W3C - 10 Status Code Definitions
  2. W3C - 14 Header Field Definitions

Dotnet-Interviews