Wednesday, October 6, 2010

log4net versions deployment issue

So few days ago I faced with issue of 3rd party references.

My original question on stackoverflow:

What is the best approach to use 3rd party that uses another version of other 3rd party (log4net) already used in the system?

  • Currently we use log4net of version 1.2.10.0 and we should start using some 3rd party components developed by other team.
  • Mentioned component references log4net of version 1.2.9.0.
  • All binaries are deployed into one folder.
I'm sure that we cannot rebuild our sources with 1.2.9.0 version, because there are too many other dependencies and will require lot of efforts. Are there any other approaches to solve this issue? I'm NOT looking for too sophisticated that have something to do with CLR assemblies loading, but would hear them with great pleasure. I'm looking for the simplest approaches. I guess someone has encountered the same issue.

I got (as for now) two answers and I would like to try them out.

So I created 3 projects, one references log4net of version of 1.2.10.0 and another references 1.2.9.0. Both of them are referenced in client console application, which also references one of the log4net assemblies. In client application I'm trying to execute code that requires log4net in both of the assemblies.

Below is projects layout:

When I execute my code I'm getting error:

Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'log4net, Version=1.2.9.0, Culture=neutral, PublicKeyToken=b32731d11ce58905' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
File name: 'log4net, Version=1.2.9.0, Culture=neutral, PublicKeyToken=b32731d11ce58905'
   at ProjReferencesLog4Net._1._2._9._0.ClassA..ctor()
   at ConsoleAppReferencesLog4Net1._2._10._0_andBothAssemblies.Program.Main(String[] args) in
  ....

In order to resolve this I tried suggestion one by one...

Suggestion number 1

Redirecting Assembly Versions

Accordingly to MSDN there is possibility to redirect code execution to assembly with higher version, just with using following configuration:

 
<configuration>
   <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
       <dependentAssembly>
         <assemblyIdentity name="log4net"
                           publicKeyToken="b32731d11ce58905"
                           culture="neutral" />
         <bindingRedirect oldVersion="1.2.9.0"
                          newVersion="1.2.10.0"/>
       </dependentAssembly>
      </assemblyBinding>
   </runtime>
</configuration>
 
I've tried it and it did not work. Reason is that we cannot do redirection between assemblies with different PublicKeyToken-s. log4net 1.2.9.0 has "b32731d11ce58905" and log4net 1.2.10.0 has "1b44e1d426115821". Also see this stackoverflow question.

Suggestion number 2

Use GAC. So when I install those two assemblies into GAC:



In this case code works, but suggestion doesn't work for us, since we do not want to gac assemblies we use.

Other suggestions

So I've been thinking about another approach.
Approaches that require rebuilding our code with different version of log4net are not suitable for us. At least for now.
Another thing about which I've been thinking is to load those assemblies into different application domain or host 3rd party that uses 1.2.9.0 under different WinService. Both of these are cumbersome solutions and I like to avoid them.

YOUR SUGGESTION

If you have some ideas, could you please let me know!

[EDITED 7 Oct, 2010 11PM]

FUNNY-HAPPY-END OF THIS STORY

Do you know what is the most interesting about all of this? It is how it has finished. We contacted those guys, who developed component we now should use. They gave us know, that they were encountering issues with updating on-the-fly configuration file for log4net 1.2.10.0. By their words, new version of log4net is not capable of doing this. So they sent as simple application that demonstrates this, and indeed, after updating config when app is running, 1.2.10.0 did not catch up new configuration, but 1.2.9.0 was working just fine. This surprised me very much, so I went to this download page and downloaded latest binaries. When I tried it got working!!! Actually I guess that they simply used version of log4net buit with references to .net framework 1.1, and we should use one built with .net 2.0 (Yeah! Actually if you would download you will see.)

After all of this, they created new sub-release of their sources especially for us and they were able to fix some minor bug. Great news! Unexpected end of story! :)

13 comments:

  1. You can try to set Private path for your application and use your subdirectory for required assemblies. http://msdn.microsoft.com/en-us/library/823z9h8w.aspx

    ReplyDelete
  2. In process of trying this out... thanks...

    ReplyDelete
  3. So, I had some hopes that this will help, but it did not.

    So .net allows me use private path and define two pathes to two assemblies but with different versions of log4net and it is loaded at runtime just fine, but when my code requires another version of log4net it doesn't load it, maybe because it thinks that it already has loaded log4net and it doesn't match required one.

    Even more ordering is taken into account, so if I have following:
    < probing privatePath="log4net129;log4net1210;" / >

    it will load version 1.2.9.0 and will NOT load 1.2.10.0, but will fail once I need it.

    Switching to:
    < probing privatePath = "log4net1210;log4net129;" / >
    changes situation vise verse.

    All depends on which version I demand first :)

    Very funny!

    ReplyDelete
  4. Have you tried to post this on Stackoverflow?

    ReplyDelete
  5. Or take a look here http://stackoverflow.com/questions/1460271/how-to-use-assembly-binding-redirection-to-ignore-revision-and-build-numbers/2344624#2344624, but it is not an _easy_ solution.

    ReplyDelete
  6. Man, were you attentive when reading this blog post? :)

    It started with "My original question on stackoverflow:
    What is the best approach to use 3rd party that uses another version of other 3rd party (log4net) already used in the system?"

    ReplyDelete
  7. By that link they are loading assembly into different application domain. I said that this is cumbersome solution, but it should work of course.

    ReplyDelete
  8. take a look at a freshly new NuPack, announced day or two earlier. It's package management system (open-source) for VS10. I don't know exactly, but it should solve your problem with dependencies

    ReplyDelete
  9. How about loading the older version into a custom AppDomain?

    ReplyDelete
  10. I would suggest that you should play with AppDomains a bit - looks like a typical class loader issue for me :)

    Таke a look at these events that AppDomain type exposes: AssemblyLoad, AssemblyResolve, TypeResolve

    ReplyDelete
  11. Oleh, I already mentioned that we do not want to overplay with such custom things like separate AppDomain to load assemblies to.

    ReplyDelete
  12. uh, right - I missed your comment. But I think it is your only viable solution

    ReplyDelete
  13. All, just read this edit added at the bottom of post:

    FUNNY-HAPPY-END OF THIS STORY

    ReplyDelete