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>

Brak komentarzy:

Prześlij komentarz