Thursday, December 27, 2007

Referencing different versions of an assembly - Part 1 (ILMerge)

Consider the following scenario:

  image

We have one huge monolithic Visual Studio solution.

It contains two applications (App1 & App2) which references lots of other projects (of which I have drawn three - A, B & C), which in turn reference Some other project - "Infra".

Our business requires that we'll develop a new version of App1 which will require a new version of Project A and a new version for Infra.

We could have update Infra, and then update Project A as well as Project B and C and actually release a new version for the whole line of products.

But this is not such a great idea - as it will require building, testing and deploying of App2 without gaining any business advantage.

(You could think that we can just leave the previous version of App2, but actually we need to update it in this scenario to be able to deploy hotfix in case of finding a bug in Infra for example)

So what we actually need is to manage branches and versions.
This will look like this:

image 

In this diagram we have made a branch for Infra project and have two versions for it. Project A will use the newer version (V1) while others will use the older version (V2).

We have, however, an implementation problem here - as you can't hold the same project twice in on Visual Studio solution.

We can fix it by breaking out Infra project from the solution into its own solution, and reference DLLs instead projects. This will look like this:

image 
(I have left App2 outside this diagram as it not needed for the explanation any more)

This way, each project will reference the version it needs (using "Specific Version").

But is referencing DLLs actually solve the problem?

Let's look at the Bin folder of our projects:

Project A --> Bin --> Infra.dll (version 2)
Project B --> Bin --> Infra.dll (version 1)

App1 --> Bin --> Infra.dll (which version ???)

You see, windows' folders can't contain the two files with the same name...
I don't know which of them we will end with - but it doesn't matter - we need both!

At first we have came with 3 options to solve this new problem:

  1. Put the two DLLs into different sub folders under Bin (via post build script), and tell the application to probe those folders.
  2. Install the DLLs into the GAC.
    This way they won't be copied into the Bin at all.
  3. Add the DLL's version to their name (e.g. Infra-1.dll)

Each option has its pros and cons, and I won't go deeper into it.
Why bother selecting the less bad option if you can find a good one? (Thanks Pavel!)

 

ILMerge

This application knows to merge multiple .NET assemblies into a single assembly.

How would that help? See:

image

With the ILMerge solution, you can keep every thing normal (not changing Assemblies names, without the need for the GAC and without using complicated sub folders).

The only thing you need is to put post build event to do ILMerge on Project A & B.
Then reference the Merged DLLs from App1. Simple - isn't it?
 

 

You can download ILMerge here

See those docs for description of the problem:

http://msdn2.microsoft.com/en-us/library/0z1t9z56(VS.80).aspx 
http://msdn2.microsoft.com/en-us/library/3b12we19(VS.80).aspx 

 

See next parts:

Part 2
Part 3

2 comments:

Kevin Berridge said...

I've been looking for a solution to a very similar variation on this same problem. I posted briefly about it in a blog post here.

Unfortunately, your solution of using ILMerge doesn't work in my circumstance because I the App1 project to reference the infra.dll too. As soon as you add that reference you get "The type 'infra.sometype' exists in both 'mergedA.dll' and 'mergedB.dll'.

If you remove the reference to infra from App1, the errors go away and everything works great.

Sadly for me, I absolutely need to reference infra from app1. Any ideas on this?

Yonatan Leonov said...

Hi Kevin

I see that we have the same problems...

I just added Part 2 for this article. See if my suggestion solves your problem.

Actually I think about writing Part 3 too, because I'm not sure my solution stands in more complicated situations.

Please let me know what do you think.