Assembly redirection in .NET

by rahul 11/5/2011 5:30:16 PM

In this blog post, I'll discuss some of the techniques that you can use for loading the assemblies in .NET and troubleshoot the loading process in case you get stuck. Basically, you will learn…

  • How to rename an assembly and still be able to load it
  • How to redirect to a specific version
  • How to use two different versions of the DLL in the same application
  • How to use fusion log and troubleshoot while doing the above exercise

Without further ado, and scaring you with the mumbo jumbo of assemblyBinding, probing and other processes, let us get straight to action. We shall learn along the way.


    Create a new Project using Visual Studio. You can use any name, but for the sake of this post, it would be better if you stick with the ones I use. So, the project name is AssemblyLoadExample, and it is created in C:\Sample

SNAGHTML10c13395

    Open Program.cs file and replace the existing code with…

using System;
using MyClassLibrary;

namespace AssemblyLoadExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(MyClass.Foo());
        }
    }
}

    Create a new class library project in a SEPARATE instance of Visual Studio with the following details… MyClassLibrary in folder C:\Sample

SNAGHTML10c56484

    Go to Class1.cs file in the classlibrary, and delete every line of code. Replace it with…

using System.Reflection;

namespace MyClassLibrary
{
    public class MyClass
    {
        public static string Foo()
        {
            return string.Format("Returning from MyClass Version = {0}", 
Assembly.GetExecutingAssembly().GetName().ToString()); } } }

    Open AssemblyInfo.cs file and change the AssemblyVersion from 1.0.0.0 to 1.0.0.1

[assembly: AssemblyVersion("1.0.0.1")]

    Go to the project properties… and select Signing. Then click New…

image

    Provide a Strong Name key, uncheck Protect my key…. check box and click OK.

SNAGHTML10ce5806

    Compile the class library now. Let's get on with the main project -> AssemblyLoadExample
    DO NOT add a project reference. Instead, add a Reference and click Browse to locate the assembly. Locate the assembly, and click Add.

SNAGHTML10d459bb

    If you have done everything correctly so far you would have a console application that shows the following when executed.

image


    The boring part is done. Now, some interesting bits.
    Remove MyClassLibrary reference from the Project AssemblyLoadExample.
    Rename "C:\Sample\MyClassLibrary\MyClassLibrary\bin\Debug\MyClassLibrary.dll" to "C:\Sample\MyClassLibrary\MyClassLibrary\bin\Debug\MyClassLibraryV1.dll"
    Add the reference "C:\Sample\MyClassLibrary\MyClassLibrary\bin\Debug\MyClassLibraryV1.dll" to the project.
    Does the project Compile? -> Yes.
    Does the project Run? -> NO!!!
    It would throw an error message…


Unhandled Exception: System.IO.FileNotFoundException: Could not load file or ass
embly 'MyClassLibrary, Version=1.0.0.1, Culture=neutral, PublicKeyToken=9fe6f24b
59015723' or one of its dependencies. The system cannot find the file specified.

   at AssemblyLoadExample.Program.Main(String[] args)

    Why? Notice the fact that while loading the assembly it is still referencing MyClassLibrary whereas, you have already changed the name of the file to MyClassLibraryV1
    Open Fusion Log Viewer. Mine is located at…

"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\x64\FUSLOGVW.exe"

    Click Settings and change the Custom log path. Also change ensure that Log all binds to disk is checked along with Enable Custom Log path.

SNAGHTML10e5bc83

    Now, run the application AssemblyLoadExample directly from "C:\Sample\AssemblyLoadExample\AssemblyLoadExample\bin\Debug"
    Switch to Fusion Log Viewer and refresh. You should be able to see some entries there…

SNAGHTML10e8ba71

    Double click on the 2nd one which shows MyClassLibrary as Description… and it will open a browser that will show you how probing happened and why your application failed.

image

    Our job is to now help the application in finding the correct name of the assembly.
    Switch back to your AssemblyLoadExample and add an app.config file.

SNAGHTML10ec0037

    Modify your app.config file to look like the following…

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="MyClassLibrary" culture="neutral" 
publicKeyToken="9fe6f24b59015723"/> <codeBase version="1.0.0.1"
href="file:///C:/Sample/MyClassLibrary/MyClassLibrary/bin/Debug/MyClassLibraryV1.dll"/> </dependentAssembly> </assemblyBinding> </runtime> </configuration>

    Do remember to change the publicKeyToken. You can use sn -T <assemblyName.dll> or simply use the fusion
log output that you saw earlier.
    Run the application now, and you should be successfully able to see the output.

image

    Essentially, what you have just done is to use assemblyIdentity tag to figure out which assembly you want to set the redirection to. This redirection is set by <codeBase> tag as you can see above.
    Let's assume that somebody want to upgrade the DLL now.
    Copy the existing MyClassLibrary project and paste it as MyClassLibraryV2

image

    Open the solution from C:\Sample\MyClassLibraryV2
    Change the assembly version to 2.0.0.0 and change the code to…

using System.Reflection;

namespace MyClassLibrary
{
    public class MyClass
    {
        public static string Foo()
        {
            return string.Format("Giving back from MyClass Version = {0}", 
Assembly.GetExecutingAssembly().GetName().ToString()); } } }

    Recompile the class library and close visual studio.
    Switch back to AssemblyLoadExample and remove MyClassLibrary reference.
    Rename "C:\Sample\MyClassLibraryV2\MyClassLibrary\bin\Debug\MyClassLibrary.dll" to "C:\Sample\MyClassLibraryV2\MyClassLibrary\bin\Debug\MyClassLibraryV2.dll"
    Now change the App.config to the following and ensure everything works as expected…

  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="MyClassLibrary" culture="neutral" 
publicKeyToken="9fe6f24b59015723"/> <codeBase version="2.0.0.0"
href="file:///C:/Sample/MyClassLibraryV2/MyClassLibrary/bin/Debug/MyClassLibraryV2.dll"/> </dependentAssembly> </assemblyBinding> </runtime>

    As you can see, you can change the version and codefile at the same time.
    What if you want to use the different versions of the DLL in the same console?
    Let's try to add both the references.

image

    Will this compile? -> NO. Obviously, it is because of the conflict!

image

    In this case, you are out of luck since Visual Studio doesn't provide enough support. Do remember that, you will have to rename the files back to the original file names, MyClassLibrary. Naturally, the locations will differ. Before you proceed further, deploy the assemblies into the GAC and use command line to compile your application.

    First of all, change your code to look like the following…

extern alias oldMyClass;
extern alias newMyClass;
using System;

namespace AssemblyLoadExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(oldMyClass::MyClassLibrary.MyClass.Foo());
            Console.WriteLine(newMyClass::MyClassLibrary.MyClass.Foo());
        }
    }
}


    Then, go the Visual Studio command prompt and issue the following command…

csc.exe /out:"C:\Sample\AssemblyLoadExample\AssemblyLoadExample\bin\Debug\AssemblyLoadExample.exe" "C:\Sample\AssemblyLoadExample\AssemblyLoadExample\Program.cs"
/r:oldMyClass="C:\Sample\MyClassLibrary\MyClassLibrary\bin\Debug\MyClassLibrary.dll"
/r:newMyClass="C:\Sample\MyClassLibraryV2\MyClassLibrary\bin\Debug\MyClassLibrary.dll"

    Also, now that everything is getting referenced directly from GAC, you will need to clear off your app.config.
    Run your application now…

Returning from MyClass Version = MyClassLibrary, Version=1.0.0.1, Culture=neutral, PublicKeyToken=9fe6f24b59015723
Giving back from MyClass Version = MyClassLibrary, Version=2.0.0.0, Culture=neutral, PublicKeyToken=9fe6f24b59015723

    There you go… both the versions in the same application.

Hope this helps, Wave
Rahul

 


Quote of the day:
Some people will never learn anything because they understand everything too soon. - Alexander Pope


blog comments powered by Disqus

Rahul Soni

Rahul Soni  Twitter

 LinkedIn

 Facebook

 Email me



Vivek Kumbhar

Vivek Kumbhar  Twitter

 LinkedIn

 Facebook

 Email me


Stack Exchange

profile for Vivek at Server Fault, Q&A for system administrators and IT professionals

profile for Rahul Soni at Stack Overflow, Q&A for professional and enthusiast programmers

Calendar

<<  February 2012  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
2728291234
567891011

View posts in large calendar

All Items
Sign in

Visit Microsoft's Site

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.
© Copyright 2012, Rahul Soni

Powered by BlogEngine.NET 1.4.5.0