Pokazywanie postów oznaczonych etykietą Config. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą Config. Pokaż wszystkie posty

poniedziałek, 28 czerwca 2010

Problem zależności pomiędzy zewnątrznymi bibliotekami

Wykorzystując zewnętrzne biblioteki można bardzo łatwo wpaść w pułapkę niezgodności ich wersji. Dla przykładu nasza aplikacja wykorzystuje biblioteki A i B. Każda z nich do pracy potrzebuje biblioteki C. Ale A używa wersji 1.2, a B 1.5. Może to doprowadzić do następującego błędu kompilacji (i to w najlepszym przypadku):

Assembly 'B, Version=1.0.0.000, Culture=neutral, PublicKeyToken=17863af14b0044da' uses 'C, Version=1.5.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc' which has a higher version than referenced assembly 'C, Version=1.2.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc'

Ostatnio sam spotkałem się z tym problemem wykorzystując w jednym projekcie możliwości AOP kontenera Autofac i NHibernate. Obydwie biblioteki były zależne od Castle.Core.dll. Zostawiając jedynie jej nowszą wersję kontener działał bez zarzutu, ale NHibernate przy próbie stworzenia proxy do encji rzucał pięknym wyjątkiem:

System.IO.FileLoadException : Could not load file or assembly 'Castle.Core, Version=1.1.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

Najprostszym rozwiązaniem problemu okazało się zmuszenie NHibernate do wykorzystywanie nowszej wersji Castle.Core poprzez tzw Assembly Binding Redirection. Sprowadza się to do dodania paru linijek w pliku *.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Castle.Core" publicKeyToken="407DD0808D44FBDC" />
        <bindingRedirect oldVersion="0.0.0.0-1.2.0.0" newVersion="1.2.0.0"/>
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Castle.DynamicProxy2" publicKeyToken="407DD0808D44FBDC" />
        <bindingRedirect oldVersion="0.0.0.0-2.2.0.0" newVersion="2.2.0.0"/>
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
Dzięki temu działa zarówno Autofac jak i NHibernate, a co najważniejsze nie trzeba nic rekompilować podmieniając zależności u źródła. Należy jednak pamiętać, że sztuczka nie zawsze musi działać. W przypadku braku zgodności między kolejnymi wersjami biblioteki mogą pojawić się inne ciekawsze błędy ;).
Warto również zauważyć, że ta metoda wykorzystywana jest w ASP MVC 2, gdzie w standardowym szablonie dla Web.config istnieje wpis:
<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
      <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/>
    </dependentAssembly>  
  </assemblyBinding>
</runtime>