Jak narzędzie NuGet rozwiązuje zależności pakietów

Za każdym razem, gdy pakiet jest instalowany lub instalowany ponownie, który obejmuje instalowanie w ramach procesu przywracania , NuGet instaluje również wszelkie dodatkowe pakiety, od których zależy ten pierwszy pakiet.

Te bezpośrednie zależności mogą mieć również zależności samodzielnie, które mogą nadal mieć dowolną głębokość. Powoduje to utworzenie grafu zależności opisującego relacje między pakietami na wszystkich poziomach.

Jeśli wiele pakietów ma tę samą zależność, ten sam identyfikator pakietu może pojawić się w grafie wiele razy, potencjalnie z różnymi ograniczeniami wersji. Jednak tylko jedna wersja danego pakietu może być używana w projekcie, więc NuGet musi wybrać, która wersja jest używana. Dokładny proces zależy od używanego formatu zarządzania pakietami.

Rozwiązywanie zależności za pomocą funkcji PackageReference

Podczas instalowania pakietów w projektach przy użyciu formatu PackageReference NuGet dodaje odwołania do prostego grafu pakietów w odpowiednim pliku i rozwiązuje konflikty z wyprzedzeniem. Ten proces jest nazywany przywracaniem przechodnim. Ponowne instalowanie lub przywracanie pakietów jest następnie procesem pobierania pakietów wymienionych na wykresie, co powoduje szybsze i bardziej przewidywalne kompilacje.

Możesz również skorzystać z wersji zmiennoprzecinkowych, takich jak 2.8.*, aby uniknąć modyfikowania projektu w celu korzystania z najnowszej wersji pakietu. W przypadku korzystania z wersji zmiennoprzecinkowych zalecamy włączenie funkcji blokady pliku w celu zapewnienia powtarzalności.

Gdy proces przywracania NuGet jest uruchamiany przed kompilacją, najpierw rozpoznaje zależności w pamięci, a następnie zapisuje wynikowy graf do pliku o nazwie project.assets.json.

Plik zasobów znajduje się w MSBuildProjectExtensionsPathlokalizacji , która jest domyślnie ustawiona na folder "obj" projektu. Program MSBuild odczytuje ten plik i tłumaczy go na zestaw folderów, w których można znaleźć potencjalne odwołania, a następnie dodaje je do drzewa projektu w pamięci.

Plik project.assets.json jest tymczasowy i nie należy go dodawać do kontroli źródła. Jest on domyślnie wyświetlany zarówno w systemach , jak .gitignore i .tfignore. Zobacz Pakiety i kontrola źródła.

Reguły rozwiązywania zależności

Przywracanie przejściowe stosuje cztery główne reguły do rozwiązywania zależności: najniższa odpowiednia wersja, wersje zmiennoprzecinkowe, bezpośrednie zależności-wins i zależności kuzyna.

Najniższa odpowiednia wersja

Najniższa odpowiednia reguła wersji przywraca najniższą możliwą wersję pakietu zgodnie z jego zależnościami. Dotyczy to również zależności aplikacji lub biblioteki klas, chyba że zadeklarowane jako zmiennoprzecinkowe.

Na poniższym rysunku, na przykład wersja 1.0-beta jest uznawana za niższą niż 1.0, więc NuGet wybiera wersję 1.0:

Choosing the lowest applicable version

Na następnej ilustracji wersja 2.1 nie jest dostępna w kanale informacyjnym, ale ponieważ ograniczenie wersji to >= 2.1 NuGet wybiera następną najniższą wersję, która może znaleźć, w tym przypadku 2.2:

Choosing the next lowest version available on the feed

Gdy aplikacja określa dokładny numer wersji, taki jak 1.2, który nie jest dostępny w kanale informacyjnym, program NuGet kończy się niepowodzeniem z powodu błędu podczas próby zainstalowania lub przywrócenia pakietu:

NuGet generates an error when an exact package version is not available

Wersje zmiennoprzecinkowe

Zmienna wersja zależności jest określona z znakiem * . Na przykład 6.0.*. Ta specyfikacja wersji mówi "użyj najnowszej wersji 6.0.x"; 4.* oznacza "użyj najnowszej wersji 4.x". Użycie wersji zmiennoprzecinkowej zmniejsza zmiany w pliku projektu, zachowując jednocześnie aktualność najnowszej wersji zależności. Wersje zmiennoprzecinkowe można określić tylko na poziomie projektu.

W przypadku korzystania z wersji przestawnej program NuGet rozpoznaje najwyższą wersję pakietu zgodnego ze wzorcem wersji, na przykład 6.0.* pobiera najwyższą wersję pakietu rozpoczynającego się od wersji 6.0:

Choosing version 6.0.1 when a floating version 6.0.* is requested

Wersja Wersje obecne na serwerze Rozwiązanie Przyczyna Uwagi
* 1.1.0
1.1.1
1.2.0
1.3.0-alfa
1.2.0 Najwyższa stabilna wersja.
1.1.* 1.1.0
1.1.1
1.1.2-alfa
1.2.0-alfa
1.1.1 Najwyższa stabilna wersja, która uwzględnia określony wzorzec.
*-* 1.1.0
1.1.1
1.1.2-alfa
1.3.0-beta
1.3.0-beta Najwyższa wersja, w tym nie stabilne wersje. Dostępne w programie Visual Studio w wersji 16.6, NuGet w wersji 5.6, zestawu .NET Core SDK w wersji 3.1.300
1.1.*-* 1.1.0
1.1.1
1.1.2-alfa
1.1.2-beta
1.3.0-beta
1.1.2-beta Najwyższa wersja szanując wzorzec i w tym nie stabilne wersje. Dostępne w programie Visual Studio w wersji 16.6, NuGet w wersji 5.6, zestawu .NET Core SDK w wersji 3.1.300

Uwaga

Rozpoznawanie wersji zmiennoprzecinkowych nie uwzględnia tego, czy na liście znajduje się pakiet. Rozpoznawanie wersji zmiennoprzecinkowych zostanie rozwiązane lokalnie, jeśli warunki mogą być spełnione z pakietami w folderze pakietu globalnego.

Bezpośrednie zwycięstwo zależności

Gdy graf pakietu dla aplikacji zawiera różne wersje pakietu w tej samej podgrafie, a jedna z tych wersji jest bezpośrednią zależnością w tym podgrafie, ta wersja zostanie wybrana dla tej podgrafu, a reszta zostanie zignorowana. To zachowanie umożliwia aplikacji zastąpienie dowolnej konkretnej wersji pakietu na grafie zależności.

W poniższym przykładzie aplikacja zależy bezpośrednio od pakietu B z ograniczeniem >wersji =2.0.0. Aplikacja zależy również od pakietu A, który z kolei zależy od pakietu B, ale z ograniczeniem >=1.0.0. Ponieważ zależność od pakietu B 2.0.0 jest bezpośrednią zależnością od aplikacji na grafie, używana jest ta wersja:

Application using the Direct dependency wins rule

Ostrzeżenie

Reguła wygranych zależności bezpośrednich może spowodować obniżenie wersji pakietu, co może spowodować przerwanie innych zależności na grafie. Gdy pakiet zostanie obniżony, pakiet NuGet doda ostrzeżenie, aby powiadomić użytkownika.

Ta reguła powoduje również większą wydajność z dużym wykresem zależności. Gdy bliższa zależność w tej samej podgrafie ma wyższą wersję niż dalszą, program NuGet ignoruje tę zależność, a program NuGet ignoruje również wszystkie pozostałe zależności od tej gałęzi grafu.

Na poniższym diagramie, na przykład, ponieważ jest używany pakiet C 2.0.0, NuGet ignoruje wszystkie gałęzie w tym podgrafie, które odwołują się do wcześniejszej wersji pakietu C:

When NuGet ignores a package in the graph, it ignores that entire branch

Za pomocą tej reguły nuGet próbuje uhonorować intencję autora pakietu. Na poniższym diagramie autor pakietu A jawnie obniżył pakiet C 1.0.0 z pakietu C 2.0.0.

When a package author explicitly downgrades, NuGet honors that.

Właściciel aplikacji może zdecydować się na uaktualnienie pakietu C do wersji nowszej niż 2.0.0, co nie powoduje dalszego obniżania wersji pakietu C. W takim przypadku nie zostanie zgłoszone żadne ostrzeżenie.

When an application honor adds a direct dependency for a downgraded package, NuGet honors that.

Zależności kuzyna

Gdy różne wersje pakietów są określane w różnych podgrafach na grafie z aplikacji, NuGet używa najniższej wersji, która spełnia wszystkie wymagania dotyczące wersji (podobnie jak w przypadku najniższych odpowiednich wersji i reguł wersji zmiennoprzecinkowych). Na poniższej ilustracji na przykład wersja 2.0.0 pakietu B spełnia pozostałe >ograniczenie =1.0.0 i w ten sposób jest używane:

Resolving cousin dependencies using the lower version that satisfies all constraints

Należy pamiętać, że pakiety nie muszą znajdować się na tej samej odległości, aby reguła zależności kuzyna miała zastosowanie. Na poniższym diagramie pakiet D 2.0.0 jest wybierany w podgrafie Package C, a pakiet D 3.0.0 jest wybierany w podgrafie Pakietu A. W podgrafie Aplikacja nie ma bezpośredniej zależności od pakietu D, więc jest stosowana najniższa odpowiednia reguła wersji i wybierana jest wersja 3.0.0.

Resolving cousin dependencies using the lower version that satisfies all constraints at different distances

W niektórych przypadkach nie można spełnić wszystkich wymagań dotyczących wersji. Jak pokazano poniżej, jeśli pakiet A wymaga dokładnie pakietu B 1.0.0 i pakietu C wymaga pakietu B >=2.0.0, nuGet nie może rozpoznać zależności i daje błąd.

Unresolvable dependencies due to an exact version requirement

W takich sytuacjach odbiorca najwyższego poziomu (aplikacja lub pakiet) powinien dodać własną bezpośrednią zależność od pakietu B, aby reguła wygranej zależności bezpośredniej dotyczyła.

Rozwiązywanie zależności za pomocą pliku packages.config

W programie packages.configzależności projektu są zapisywane packages.config jako płaska lista. Wszystkie zależności tych pakietów są również zapisywane na tej samej liście. Po zainstalowaniu .csproj pakietów pakiet NuGet może również modyfikować plik, app.config, web.configi inne pojedyncze pliki.

W programie packages.configNuGet próbuje rozwiązać konflikty zależności podczas instalacji każdego pojedynczego pakietu. Oznacza to, że jeśli pakiet A jest instalowany i zależy od pakietu B, a pakiet B jest już wymieniony jako packages.config zależność czegoś innego, NuGet porównuje żądane wersje pakietu B i próbuje znaleźć wersję, która spełnia wszystkie ograniczenia wersji. W szczególności nuGet wybiera niższą wersję główną.pomocniczą , która spełnia zależności.

Domyślnie nuGet 2.8 szuka najniższej wersji poprawki (zobacz Informacje o wersji NuGet 2.8). To ustawienie można kontrolować za pomocą atrybutu DependencyVersion w i NuGet.Config-DependencyVersion przełącznika w wierszu polecenia.

Proces rozwiązywania packages.config zależności jest skomplikowany w przypadku większych grafów zależności. Każda nowa instalacja pakietu wymaga przejścia całego grafu i zwiększa prawdopodobieństwo konfliktów wersji. Gdy wystąpi konflikt, instalacja zostanie zatrzymana, pozostawiając projekt w stanie nieokreślonym, zwłaszcza z potencjalnymi modyfikacjami samego pliku projektu. Nie jest to problem podczas korzystania z innych formatów zarządzania pakietami.

Zarządzanie zasobami zależności

W przypadku korzystania z formatu PackageReference można kontrolować, które zasoby z zależności przepływają do projektu najwyższego poziomu. Aby uzyskać szczegółowe informacje, zobacz PackageReference.

Gdy projekt najwyższego poziomu jest pakietem, masz również kontrolę nad tym przepływem przy użyciu include atrybutów i exclude z zależnościami wymienionymi w .nuspec pliku. Zobacz .nuspec Reference - Dependencies (Dokumentacja narzędzia .nuspec — zależności).

Wykluczanie odwołań

Istnieją scenariusze, w których zestawy o tej samej nazwie mogą być przywoływały więcej niż raz w projekcie, tworząc błędy czasu projektowania i czasu kompilacji. Rozważ projekt zawierający niestandardową wersję C.dllelementu i odwołuje się do pakietu C, który zawiera C.dllrównież element . Jednocześnie projekt zależy również od pakietu B, który również zależy od pakietu C i C.dll. W związku z tym pakiet NuGet nie może określić, którego C.dll z nich użyć, ale nie można po prostu usunąć zależności projektu od pakietu C, ponieważ pakiet B również zależy od niego.

Aby rozwiązać ten problem, musisz bezpośrednio odwołać się do żądanego C.dll pakietu (lub użyć innego pakietu odwołującego się do odpowiedniego), a następnie dodać zależność od pakietu C, który wyklucza wszystkie jego zasoby. Odbywa się to w następujący sposób w zależności od używanego formatu zarządzania pakietami:

  • PackageReference: dodaj ExcludeAssets="All" zależność:

    <PackageReference Include="PackageC" Version="1.0.0" ExcludeAssets="All" />
    
  • packages.config: usuń odwołanie do packageC z .csproj pliku, aby odwoływać się tylko do żądanej C.dll wersji.

Aktualizacje zależności podczas instalowania pakietu

Jeśli wersja zależności jest już spełniona, zależność nie jest aktualizowana podczas innych instalacji pakietów. Rozważmy na przykład pakiet A, który zależy od pakietu B i określa numer wersji 1.0. Repozytorium źródłowe zawiera wersje 1.0, 1.1 i 1.2 pakietu B. Jeśli element A jest zainstalowany w projekcie, który zawiera już wersję B 1.0, usługa B 1.0 pozostaje w użyciu, ponieważ spełnia ograniczenie wersji. Jeśli jednak pakiet A miał wersję 1.1 lub nowszą B, zostanie zainstalowany pakiet B 1.2.

Rozwiązywanie problemów z niezgodnymi błędami pakietu

Podczas operacji przywracania pakietu może zostać wyświetlony błąd "Co najmniej jeden pakiet nie jest zgodny..." lub że pakiet "nie jest zgodny" z platformą docelową projektu.

Ten błąd występuje, gdy co najmniej jeden pakiet, do którego odwołuje się projekt, nie wskazuje na to, że obsługują strukturę docelową projektu; oznacza to, że pakiet nie zawiera odpowiedniej biblioteki DLL w folderze lib dla platformy docelowej zgodnej z projektem. (Zobacz Platformy docelowe dla listy).

Jeśli na przykład projekt jest docelowy netstandard1.6 i próbujesz zainstalować pakiet zawierający biblioteki DLL tylko lib\net20 w folderach i \lib\net45 , zobaczysz komunikaty podobne do następujących dla pakietu i prawdopodobnie jego zależności:

Restoring packages for myproject.csproj...
Package ContosoUtilities 2.1.2.3 is not compatible with netstandard1.6 (.NETStandard,Version=v1.6). Package ContosoUtilities 2.1.2.3 supports:
  - net20 (.NETFramework,Version=v2.0)
  - net45 (.NETFramework,Version=v4.5)
Package ContosoCore 0.86.0 is not compatible with netstandard1.6 (.NETStandard,Version=v1.6). Package ContosoCore 0.86.0 supports:
  - 11 (11,Version=v0.0)
  - net20 (.NETFramework,Version=v2.0)
  - sl3 (Silverlight,Version=v3.0)
  - sl4 (Silverlight,Version=v4.0)
One or more packages are incompatible with .NETStandard,Version=v1.6.
Package restore failed. Rolling back package changes for 'MyProject'.

Aby rozwiązać problemy z niezgodnościami, wykonaj jedną z następujących czynności:

  • Retarget your project to a framework that is supported by the packages you want to use( Retarget your project to a framework that is supported by the packages you want to use.
  • Skontaktuj się z autorem pakietów i skontaktuj się z nimi, aby dodać obsługę wybranej platformy. W tym celu każda strona listy pakietów na nuget.org ma link Skontaktuj się z właścicielami .

Napiwek

Rozwiązanie alternatywne: NuGetSolver to rozszerzenie programu Visual Studio opracowane przez firmę Microsoft DevLabs, które ułatwia rozwiązywanie konfliktów zależności. Automatyzuje proces identyfikowania i rozwiązywania tych problemów. Aby uzyskać więcej informacji, odwiedź stronę NuGetSolver w witrynie Visual Studio Marketplace i chętnie poznamy Twoją opinię na temat Twojego środowiska.