Wersjonowanie zestawów w .NET może być mylącą perspektywą, biorąc pod uwagę, że istnieją obecnie co najmniej trzy sposoby określenia wersji dla zestawu.
Oto trzy główne atrybuty zespołu związane z wersją:
// Assembly mscorlib, Version 2.0.0.0
[assembly: AssemblyFileVersion("2.0.50727.3521")]
[assembly: AssemblyInformationalVersion("2.0.50727.3521")]
[assembly: AssemblyVersion("2.0.0.0")]
Umownie, cztery części wersji określane są jako główna wersja , wersja pomocnicza , produkcji i rewizji .
Ma AssemblyFileVersion
on na celu jednoznaczną identyfikację kompilacji poszczególnych zespołów
Zazwyczaj ręcznie ustawiasz główny i mniejszy plikFiltVersion tak, aby odzwierciedlał wersję zestawu, a następnie zwiększaj liczbę kompilacji i / lub wersji za każdym razem, gdy system kompilacji kompiluje zestaw. AssemblyFileVersion powinien umożliwiać unikalną identyfikację kompilacji zestawu, dzięki czemu można go użyć jako punktu wyjścia do debugowania wszelkich problemów.
W moim bieżącym projekcie serwer kompilacji koduje numer listy zmian z naszego repozytorium kontroli źródła do części kompilacji i wersji AssemblyFileVersion. To pozwala nam mapować bezpośrednio z zestawu do jego kodu źródłowego, dla dowolnego zestawu wygenerowanego przez serwer kompilacji (bez konieczności używania etykiet lub rozgałęzień w kontroli źródła lub ręcznego przechowywania jakichkolwiek zapisów wydanych wersji).
Ten numer wersji jest przechowywany w zasobie wersji Win32 i można go zobaczyć podczas przeglądania stron właściwości Eksploratora Windows dla zestawu.
CLR nie przejmuje się ani nie sprawdza AssemblyFileVersion.
Ma AssemblyInformationalVersion
on reprezentować wersję całego produktu
AssemblyInformationalVersion ma na celu umożliwienie spójnego wersjonowania całego produktu, który może składać się z wielu zestawów, które są niezależnie wersjonowane, być może z różnymi politykami wersjonowania i potencjalnie opracowywanych przez różne zespoły.
„Na przykład wersja 2.0 produktu może zawierać kilka zestawów; jeden z tych zespołów jest oznaczony jako wersja 1.0, ponieważ jest to nowy zespół, który nie został dostarczony w wersji 1.0 tego samego produktu. Zazwyczaj główne i podrzędne części tego numeru wersji są ustawione jako publiczna wersja Twojego produktu. Następnie zwiększasz części kompilacji i wersji za każdym razem, gdy pakujesz kompletny produkt ze wszystkimi jego zespołami. ” - Jeffrey Richter, [CLR przez C # (wydanie drugie)] str. 57
CLR nie przejmuje się ani nie analizuje AssemblyInformationalVersion.
Jest AssemblyVersion
to jedyna wersja, na której zależy CLR (ale dba o całość AssemblyVersion
)
AssemblyVersion jest używany przez CLR do wiązania z silnie nazwanymi zestawami. Jest on przechowywany w tabeli metadanych manifestu AssemblyDef zestawu złożonego oraz w tabeli AssemblyRef każdego zestawu, który się do niego odwołuje.
Jest to bardzo ważne, ponieważ oznacza to, że gdy odwołujesz się do mocno nazwanego zestawu, jesteś ściśle związany z konkretną wersją AssemblyVersion tego zestawu. Całe AssemblyVersion musi być dokładnie dopasowane, aby powiązanie zakończyło się powodzeniem. Na przykład, jeśli odwołujesz się do wersji 1.0.0.0 silnie nazwanego zestawu w czasie kompilacji, ale tylko wersja 1.0.0.1 tego zestawu jest dostępna w czasie wykonywania, wiązanie nie powiedzie się! (Następnie trzeba będzie obejść ten problem przy użyciu przekierowania wiązania zespołu ).
Zamieszanie co do tego, czy całość AssemblyVersion
musi się zgadzać. (Tak.)
Istnieje niewielkie zamieszanie wokół tego, czy całe AssemblyVersion musi być dokładnie dopasowane, aby zespół mógł zostać załadowany. Niektórzy ludzie są w fałszywym przekonaniu, że tylko główne i mniejsze części AssemblyVersion muszą się zgadzać, aby wiązanie się powiodło. Jest to rozsądne założenie, jednak ostatecznie jest niepoprawne (od .NET 3.5) i weryfikacja tego w przypadku wersji CLR jest banalna. Po prostu uruchom ten przykładowy kod .
Na mojej maszynie ładowanie drugiego zestawu kończy się niepowodzeniem, a ostatnie dwa wiersze dziennika zgrzewania doskonale wyjaśniają, dlaczego:
.NET Framework Version: 2.0.50727.3521
---
Attempting to load assembly: Rhino.Mocks, Version=3.5.0.1337, Culture=neutral, PublicKeyToken=0b3305902db7183f
Successfully loaded assembly: Rhino.Mocks, Version=3.5.0.1337, Culture=neutral, PublicKeyToken=0b3305902db7183f
---
Attempting to load assembly: Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, PublicKeyToken=0b3305902db7183f
Assembly binding for failed:
System.IO.FileLoadException: Could not load file or assembly 'Rhino.Mocks, Version=3.5.0.1336, Culture=neutral,
PublicKeyToken=0b3305902db7183f' or one of its dependencies. The located assembly's manifest definition
does not match the assembly reference. (Exception from HRESULT: 0x80131040)
File name: 'Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, PublicKeyToken=0b3305902db7183f'
=== Pre-bind state information ===
LOG: User = Phoenix\Dani
LOG: DisplayName = Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, PublicKeyToken=0b3305902db7183f
(Fully-specified)
LOG: Appbase = [...]
LOG: Initial PrivatePath = NULL
Calling assembly : AssemblyBinding, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v2.0.50727\config\machine.config.
LOG: Post-policy reference: Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, PublicKeyToken=0b3305902db7183f
LOG: Attempting download of new URL [...].
WRN: Comparing the assembly name resulted in the mismatch: Revision Number
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.
Myślę, że przyczyną tego zamieszania jest prawdopodobnie to, że Microsoft pierwotnie zamierzał być nieco łagodniejszy w tym ścisłym dopasowaniu pełnego zestawu AssemblyVersion, dopasowując tylko części w wersji Major i Minor:
„Podczas ładowania zestawu CLR automatycznie znajdzie najnowszą zainstalowaną wersję serwisową, która odpowiada głównej / podrzędnej wersji żądanego zestawu.” - Jeffrey Richter, [CLR przez C # (wydanie drugie)] str. 56
Takie zachowanie zachodziło w wersji Beta 1 CLR 1.0, jednak ta funkcja została usunięta przed wydaniem 1.0 i nie udało jej się ponownie wyświetlić w .NET 2.0:
„Uwaga: Właśnie opisałem, jak powinieneś myśleć o numerach wersji. Niestety CLR nie traktuje numerów wersji w ten sposób. [W .NET 2.0] CLR traktuje numer wersji jako wartość nieprzezroczystą, a jeśli zestaw zależy od wersji 1.2.3.4 innego zestawu, CLR próbuje załadować tylko wersję 1.2.3.4 (chyba że istnieje przekierowanie wiązania ). Jednak
Microsoft planuje zmienić moduł ładujący CLR w przyszłej wersji, aby ładował najnowszą kompilację / wersję dla danej głównej / mniejszej wersji zestawu. Na przykład w przyszłej wersji CLR, jeśli moduł ładujący próbuje znaleźć wersję 1.2.3.4 zestawu i istnieje wersja 1.2.5.0, moduł ładujący z automatycznie pobiera najnowszą wersję serwisową. Będzie to bardzo mile widziana zmiana w module ładującym CLR - ja nie mogę się doczekać. ” - Jeffrey Richter, [CLR przez C # (wydanie drugie)] str. 164 (moje podkreślenie)
Ponieważ ta zmiana nadal nie została zaimplementowana, uważam, że można bezpiecznie założyć, że Microsoft dokonał wstecznej oceny tego zamiaru i być może jest już za późno, aby to zmienić. Próbowałem przeszukać sieć, aby dowiedzieć się, co się stało z tymi planami, ale nie mogłem znaleźć odpowiedzi. Nadal chciałem dojść do sedna.
Wysłałem więc e-maila do Jeffa Richtera i zapytałem go bezpośrednio - pomyślałem, że jeśli ktokolwiek wie, co się stanie, to będzie on.
Odpowiedział w ciągu 12 godzin, nie mniej w sobotę rano, i wyjaśnił, że moduł ładujący .NET 1.0 Beta 1 wdrożył ten mechanizm „automatycznego przechodzenia do przodu” polegający na pobieraniu najnowszej dostępnej kompilacji i wersji zestawu, ale takie zachowanie było cofnięte przed wysłaniem .NET 1.0. Później zamierzano to przywrócić, ale nie udało się to przed wysłaniem CLR 2.0. Potem pojawił się Silverlight, który miał priorytet dla zespołu CLR, więc ta funkcja uległa dalszemu opóźnieniu. W międzyczasie większość ludzi, którzy byli w pobliżu w czasach CLR 1.0 Beta 1, przeprowadziła się, więc jest mało prawdopodobne, że ujrzy światło dzienne, pomimo całej ciężkiej pracy, którą już w to włożyliśmy.
Wydaje się, że obecne zachowanie pozostanie.
Warto również zauważyć z mojej dyskusji z Jeffem, że AssemblyFileVersion został dodany dopiero po usunięciu mechanizmu „automatycznego przechodzenia do przodu” - ponieważ po 1.0 Beta 1 każda zmiana w AssemblyVersion była przełomową zmianą dla twoich klientów, wtedy było nigdzie nie można bezpiecznie zapisać numeru kompilacji. AssemblyFileVersion to bezpieczna przystań, ponieważ nigdy nie jest automatycznie sprawdzana przez CLR. Być może jest to w ten sposób jaśniejsze, mając dwa oddzielne numery wersji, z odrębnymi znaczeniami, zamiast próbować dokonać tego oddzielenia między częściami Major / Minor (łamanie) i Build / Revision (niełamanie) w AssemblyVersion.
Podsumowując: Zastanów się, kiedy zmienisz swój AssemblyVersion
Morał polega na tym, że jeśli wysyłasz zestawy, do których będą się odwoływać inni programiści, musisz zachować szczególną ostrożność, kiedy zmieniasz (i nie zmieniasz) wersję AssemblyVersion tych zestawów. Wszelkie zmiany w AssemblyVersion będą oznaczać, że programiści aplikacji będą musieli albo ponownie skompilować z nową wersją (aby zaktualizować te wpisy AssemblyRef), albo użyć przekierowań powiązania zestawu, aby ręcznie zastąpić powiązanie.
- Nie zmieniaj AssemblyVersion dla wydania serwisowego, które ma być kompatybilne wstecz.
- Czy zmienić AssemblyVersion dla uwolnienia że wiesz posiada łamanie zmian.
Jeszcze raz spójrz na atrybuty wersji na mscorlib:
// Assembly mscorlib, Version 2.0.0.0
[assembly: AssemblyFileVersion("2.0.50727.3521")]
[assembly: AssemblyInformationalVersion("2.0.50727.3521")]
[assembly: AssemblyVersion("2.0.0.0")]
Zauważ, że to AssemblyFileVersion zawiera wszystkie interesujące informacje dotyczące serwisowania (jest to wersja Revision tej wersji, która mówi ci, na którym dodatku Service Pack jesteś), tymczasem AssemblyVersion jest naprawiony na nudnym starym 2.0.0.0. Każda zmiana w AssemblyVersion zmusiłaby każdą aplikację .NET odwołującą się do mscorlib.dll do ponownej kompilacji z nową wersją!