views:

54

answers:

2

I am working on a project that uses conflict.dll version 6.2, but the project also uses helper.dll that uses conflict.dll version 5.8.

I could install 6.2 and 5.8 into the GAC, but I'ld like to have this project xcopy deployable. I believe .net will search for the assemblies in the application bin directory like so: \bin\conflict.dll (6.2) \bin\5.8\conflict.dll (5.8)

But at this point, how do I add a reference to both versions of conflict.dll in the project, and then how do I make sure the old conflict.dll deploys to \bin\5.8? Do I create a build action or is there another way?

Thanks

+2  A: 

I believe .net will search for the assemblies in the application bin directory like so: \bin\conflict.dll (6.2) \bin\5.8\conflict.dll (5.8)

No, this is wrong. I would suggest you reading this article to learn more about what heuristics does the CLR use for probing.

This being said, you cannot have two different versions of the same assembly loaded in the same application domain you can load different versions of the same assembly into the same application domain but it is considered bad practice and should be avoided. In your case this means that you will have to choose which version of the conflicting assembly you want to use. You have a couple of choices:

  1. Recompile helper.dll to use the latest version of conflict.dll (I will definetely go with this one if Ihave the source code for helper.dll).
  2. Choose which version of conflict.dll you want and apply a <bindingRedirect> in your config file. For example if you want to use the latest version:

    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
                <assemblyIdentity name="conflict"
                                  publicKeyToken="xxxxxxxxx"
                                  culture="neutral" />
                <bindingRedirect oldVersion="5.8.0.0" 
                                 newVersion="6.2.0.0" />
            </dependentAssembly>
        </assemblyBinding>
    </runtime>
    

This will effectively instruct the CLR to load version 6.2 of conflict.dll when it tries to resolve references for helper.dll. Note that if the two versions are strongly signed with different keys this technique won't work. Obviously as helper.dll has been compiled against version 5.8 if you have any differences (missing methods, different method signatures) you will get a runtime exception when trying to call a conflicting method so do this only if you are absolutely sure what you are doing.

Conclusion: no matter which path you decide to take, you will have to xcopy in the bin folder a single version of the conflict.dll.

Darin Dimitrov
@darin - sorry but 'you cannot have two different versions of the same assembly loaded in the same application domain' is incorrect. Not only can you have two loaded at the same time (and use them independently) you can also load the same assembly more than once into an application domain and use them independently. Try using `Assembly.LoadFrom` to load an identical assembly from two different locations - then call the `AppDomain.CurrentDomain.GetAssemblies()`
Andras Zoltan
@Andras, you are correct, I will update my post to reflect your comment. Thanks for pointing this out.
Darin Dimitrov
@Darin +1 in any case for offering a solution that'll also help get the house in order :)
Andras Zoltan
I see why this is a bad practice, I just had a runtime exception trying to use a bindingRedirect because obviously the interfaces within the assemblies have changed and helper.dll is freaking out.
djmc
I will see if I can recompile the source code. Thank you very much for your answer.. you've helped me understand what's going on here better.
djmc
@djmc, if the interfaces have changed, recompiling the code won't be straightforward. I hope that there are no fundamental changes and you will easily achieve this.
Darin Dimitrov
it's true.. but I don't have any other choice if I cant run the old and new version of the assemblies side by side.
djmc
+1  A: 

In support of Darin's answer of course you need to be eliminating such multi-version problems. His solution of using a binding redirect is a good one - +1 there. I can offer a solution that'll allow you to keep both if absolutely necessary, but you'll have to write a bit of code.

The only real problem you have here is that the two deployed filenames would have to be the same in order to be picked up by default by the loader. You could cheat really horribly and simply deploy the 5.8 dll as Conflict.exe so it could sit side by side Conflict.dll (and newer) and you'd find that it works.

Also, following the links through from Darin's answer you come to this topic MSDN topic on probing. Based on the content of this, you could simply deploy the 5.8 dll into bin\Content\Content.dll and when the runtime searches for it, it will look in this subfolder automatically.

However - that isn't a good solution :)

EDIT - NEW SOLUTION

If both versions of Conflict.dll are already signed, have you actually tried deploying one of the versions with a slightly different name? I've just set up a winforms app with two assembly references to different versions of the same (signed) assembly. This causes a couple of problems with the build, because the last-referenced version will be deployed to the bin folder, and the other one will not (so you have to manually copy in both; renaming one of them accordingly). Then I try running the app, which displays a message box containing two constant strings; one from each version of the assembly. It works absolutely fine.

Download a demo of this here - don't build it (otherwise you have to do the file renaming); just open the forms app's bin\debug folder and run the exe.

ClassLibrary1.dll and ClassLibary1vanything.dll are v1.0.0.0 and v2.0.0.0 of an assembly with otherwise the same name and public key. Despite the fact that classlibrary1vanything.dll has the wrong filename, it still works (probably because it is signed).

In the app.config I did put in a codebase hint, and thought that was why it worked (originally I deployed it as a different filename), but then I commented it out and it still worked. The codebase is probably most useful instead when the assembly has to be deployed to a sub folder or completely different location.

ORIGINAL TEXT

I've tried to get the second of the options mentioned in this support article from MS to work, but it doesn't seem to want to.

There is no doubt some clever way to do it out of the box, but since I'm not clever enough to have found it (yet), I would instead embellish and use the third of the options displayed in the aforementioned support topic and hook into the AssemblyResolve event of the app domain.

If you add your own configuration (probably just in appSettings really) for the full name of the assembly to be bound to a different filename, then in your AssemblyResolve event handler you can consult the name of the assembly that is to be loaded to see if it's in your configuration. If it is, grab hold of the location and use Assembly.LoadFrom to load it.

Thus, once you have something like this in place, you simply add an entry for the Conflict v5.8 assembly name in there along with the filename that the app should use.

I don't know what type of app it is that you're deploying but in win forms, console apps and services AppDomain.CurrentDomain.BaseDirectory will be equal to the bin folder and you can join that with the filename you want to load. Websites are a little trickier.

Should work a treat.

Andras Zoltan
This could help me for this weekend. Great answer.. I wont have access to the source code to recompile helper.dll till about mid week next week so this hopefully will keep me productive. Thanks a lot!
djmc
@djmc - well I hope you manage to get some traction with this in place
Andras Zoltan
@djmc - just in case you haven't noticed; I updated my answer with a demo that might help you out here.
Andras Zoltan
thank you so much! it worked. Renaming the file allows it to be added to visual studio, and still loads the old version of the assembly.
djmc
Thanks for taking the time to figure this out. I really appreciate it.
djmc
@djmc - That's fantastic news, really glad I could help you out! It was an interesting problem that I just had to investigate so the time was well worth it :)
Andras Zoltan