Поделиться через


Как NuGet разрешает зависимости пакетов

При установке или переустановке пакета, включая установку в рамках процесса восстановления , NuGet также устанавливает все дополнительные пакеты, от которых зависит этот первый пакет.

Эти непосредственные зависимости могут также иметь свои собственные зависимости, которые могут продолжаться до произвольной глубины. Это создает то, что называется графом зависимостей , описывающим связи между пакетами на всех уровнях.

Если несколько пакетов имеют одинаковую зависимость, то один и тот же идентификатор пакета может отображаться в графе несколько раз, потенциально с разными ограничениями версии. Однако в проекте может использоваться только одна версия данного пакета, поэтому NuGet должна выбрать используемую версию. Точный процесс зависит от используемого формата управления пакетами.

Разрешение зависимостей с помощью PackageReference

При установке пакетов в проекты с помощью формата PackageReference NuGet добавляет ссылки на граф неструктурированных пакетов в соответствующем файле и разрешает конфликты заранее. Этот процесс называется транзитивным восстановлением. Переустановка или восстановление пакетов — это процесс скачивания пакетов, перечисленных в графе, что приводит к более быстрым и предсказуемым сборкам.

Вы также можете воспользоваться преимуществами плавающих версий, таких как 2.8.*, чтобы избежать изменения проекта, чтобы использовать последнюю версию пакета. При использовании плавающих версий рекомендуется включить функциональность файлов блокировки , чтобы обеспечить повторяемость.

Когда процесс восстановления NuGet выполняется до сборки, он сначала разрешает зависимости в памяти, а затем записывает результирующий граф в файл с именем project.assets.json.

Файл активов находится в MSBuildProjectExtensionsPath, который по умолчанию расположен в папке obj проекта. ЗАТЕМ MSBuild считывает этот файл и преобразует его в набор папок, где можно найти потенциальные ссылки, а затем добавляет их в дерево проекта в памяти.

Файл project.assets.json является временным и не следует добавлять в систему управления версиями. По умолчанию он указан как в .gitignore, так и в .tfignore. См. пакеты и системы управления версиями.

Правила разрешения зависимостей

Транзитивное восстановление применяет четыре основных правила для разрешения зависимостей: самая низкая применимая версия, плавающие версии, победа прямой зависимостии близкородственные зависимости.

Самая низкая применимая версия

Самое низкое применимое правило версии восстанавливает наименьшую возможную версию пакета, определяемую его зависимостями. Он также применяется к зависимостям приложения или библиотекам классов, если они не объявлены как с плавающей запятой.

На следующем рисунке, например, бета-версия 1.0 считается ниже 1.0, поэтому NuGet выбирает версию 1.0:

Выбор самой низкой применимой версии

На следующем рисунке версия 2.1 недоступна в канале, но поскольку ограничение версии >= 2.1, NuGet выбирает наименьшую возможную версию, в этом случае 2.2.

Выбор следующей минимальной версии, доступной на веб-канале

Если приложение указывает точный номер версии, например 1.2, который недоступен в фиде, NuGet завершается ошибкой при попытке установить, загрузить или восстановить пакет:

NuGet создает ошибку, если недоступна точная версия пакета

Плавающие версии

Плавающая версия зависимостей указывается с символом *. Например, 6.0.*. Эта спецификация версии говорит о том, что используется последняя версия 6.0.x; 4.* означает "использовать последнюю версию 4.x". Использование плавающей версии уменьшает изменения в файле проекта, сохраняя актуальность последней версии зависимости. Плавающие версии можно указать только на уровне проекта.

При использовании плавающей версии NuGet разрешает самую высокую версию пакета, соответствующую шаблону версии, например 6.0.* получает самую высокую версию пакета, начинающегося с версии 6.0:

выбор версии 6.0.1 при запросе плавающей версии 6.0.*

Версия Версии, присутствующих на сервере Резолюция Причина Примечания
* 1.1.0
1.1.1
1.2.0
1.3.0-alpha
1.2.0 Самая стабильная версия.
1.1.* 1.1.0
1.1.1
1.1.2-альфа
1.2.0-alpha
1.1.1 Самая стабильная версия, которая учитывает указанный шаблон.
*-* 1.1.0
1.1.1
1.1.2-альфа
1.3.0-beta
1.3.0-beta Самая высокая версия, включая не стабильные версии. Доступно в Visual Studio версии 16.6, NuGet версии 5.6, пакет SDK для .NET Core версии 3.1.300
1.1.*-* 1.1.0
1.1.1
1.1.2-альфа
1.1.2-beta
1.3.0-beta
1.1.2-beta Самая высокая версия, уважающая шаблон и включая не стабильные версии. Доступно в Visual Studio версии 16.6, NuGet версии 5.6, пакет SDK для .NET Core версии 3.1.300
1.2.0-rc.* 1.1.0
1.2.0-rc.1
1.2.0-rc.2
1.2.0
1.2.0 Несмотря на то, что это диапазон версий с предварительной частью, стабильные версии разрешены, если они соответствуют стабильной части версии. При условии, что 1.2.0 > 1.2.0-rc.2, выбор сделан в его пользу.

Заметка

Разрешение с плавающей версией не учитывает, указан ли пакет. Разрешение плавающей версии будет выполняться локально, если условия могут быть выполнены с помощью пакетов в глобальной папке пакетов.

Прямые зависимости имеют преимущество

Если граф пакетов для приложения содержит разные версии пакета в одном подграфе, и одна из этих версий является прямой зависимостью в этом подграфе, эта версия будет выбрана для этого подграфа, а остальные будут игнорироваться. Это поведение позволяет приложению переопределить любую определенную версию пакета в графе зависимостей.

В приведенном ниже примере приложение зависит непосредственно от пакета B с ограничением версии >=2.0.0. Приложение также зависит от пакета A, который, в свою очередь, зависит от пакета B, но с ограничением >=1.0.0. Так как зависимость от пакета B 2.0.0 является прямой зависимостью приложения в графе, эта версия используется:

приложение, использующее правило

Предупреждение

Правило побеждает прямая зависимость может привести к понижению версии пакета, что потенциально может нарушить другие зависимости в графе. При понижении уровня пакета NuGet добавляет предупреждение для оповещения пользователя.

Это правило также приводит к повышению эффективности с большим графом зависимостей. Если ближайшая зависимость в том же подграфе имеет более высокую версию, чем более дальняя, то NuGet игнорирует эту зависимость, а также все оставшиеся зависимости от этой ветви графа.

Например, на схеме ниже, так как используется пакет C 2.0.0, NuGet игнорирует все ветви в этом подграфе, ссылающиеся на более раннюю версию пакета C:

Если NuGet игнорирует пакет в графе, он игнорирует все ветви

С помощью этого правила NuGet пытается выполнить намерение автора пакета. На приведенной ниже схеме автор пакета A явно снизился до пакета C 1.0.0 из пакета C 2.0.0.

Когда автор пакета явно понижает версию, NuGet учитывает это.

Владелец приложения может обновить пакет C до версии выше 2.0.0, что предотвращает понижение версии для пакета C. В этом случае предупреждение не возникает.

Когда приложение корректно добавляет прямую зависимость для пакета с пониженной версией, NuGet учитывает это.

Зависимости двоюродных элементов

Если в разных подграфах графа приложения ссылаются на разные версии пакетов, NuGet использует самую низкую версию, которая удовлетворяет всем требованиям к версии (как по правилам наименьшей применимой версии и плавающих версий). На рисунке ниже, например, версия 2.0.0 пакета B удовлетворяет другому ограничению >=1.0.0, и поэтому используется:

Разрешение зависимостей двоюродных элементов с помощью более низкой версии, которая удовлетворяет всем ограничениям

Обратите внимание, что пакеты не должны находиться на одинаковом расстоянии, чтобы правило двоюродных зависимостей применялось. На схеме ниже выбран пакет D 2.0.0 в подграфе Package C и пакет D 3.0.0 выбран в подграфе пакета A. В подграфе приложения нет прямой зависимости от пакета D, поэтому применяется самое низкое применимое правило версии и выбрана версия 3.0.0.

разрешение зависимостей двоюродных элементов с помощью более низкой версии, которая удовлетворяет всем ограничениям на разных расстояниях

В некоторых случаях невозможно выполнить все требования к версии. Как показано ниже, если пакет A требует точно пакета B 1.0.0.0 и пакета C требует пакет B >=2.0.0, NuGet не может устранить зависимости и дает ошибку.

Неразрешенные зависимости из-за требования точной версии

В этих ситуациях потребитель верхнего уровня (приложение или пакет) должен добавить собственную прямую зависимость от пакета B, чтобы применялось правило приоритет прямой зависимости.

Диапазоны версий и предварительные версии с помощью PackageReference

Для пакета не является необычным, что доступны как стабильные, так и предварительные версии. При разрешении графа зависимостей NuGet решает, следует ли учитывать предварительные версии пакета на основе одного правила: If the project or any packages within the graph request a prerelease version of a package, then include both prerelease or stable versions, otherwise consider stable versions only.

На практике в соответствии с самым низким применимым правилом это означает:

Диапазон версий Доступные версии Выбранная версия
[1.0.0, 2.0.0) 1.2.0-beta.1, 1.2.0, 1.2.0
[1.0.0, 2.0.0-0) 1.2.0-beta.1, 1.2.0, 1.2.0-beta.1
[1.0.0, 2.0.0) 1.2.0-beta.1, 2.0.0-beta.3 Нет, вызывается NU1103.
[1.0.0, 2.0.0-rc) 1.2.0-beta.1, 2.0.0-beta.3 1.2.0-beta.1

Разрешение зависимостей с помощью packages.config

При использовании packages.configзависимости проекта записываются в packages.config как плоский список. Все зависимости этих пакетов также записываются в одном списке. При установке пакетов NuGet также может изменить файл .csproj, app.config, web.configи другие отдельные файлы.

При использовании packages.configNuGet пытается устранить конфликты зависимостей во время установки каждого отдельного пакета. То есть если пакет A устанавливается и зависит от пакета B, а пакет B уже указан в packages.config как зависимость от другого, NuGet сравнивает версии запрошенного пакета B и пытается найти версию, которая удовлетворяет всем ограничениям версий. В частности, NuGet выбирает более низкий основной.дополнительный версии, которая удовлетворяет зависимостям.

По умолчанию NuGet 2.8 ищет самую низкую версию исправления (см. заметки о выпуске NuGet 2.8). Этот параметр можно контролировать с помощью атрибута DependencyVersion в NuGet.Config и переключателя -DependencyVersion в командной строке.

Процесс packages.config для разрешения зависимостей становится сложным для более крупных графов зависимостей. Каждая новая установка пакета требует обхода всего графа и повышает вероятность конфликтов версий. При возникновении конфликта установка останавливается, оставляя проект в неопределенном состоянии, особенно с потенциальными изменениями в самом файле проекта. Это не проблема при использовании других форматов управления пакетами.

Диапазоны версий и предварительные версии с packages.config

packages.config разрешение не позволяет смешивать стабильную и предварительную зависимость в графе. Если зависимость выражается с диапазоном, например [1.0.0, 2.0.0), пакеты предварительной версии не допускаются в графе.

Управление активами зависимостей

При использовании формата PackageReference можно управлять тем, какие ресурсы из зависимостей поступают в проект верхнего уровня. Дополнительные сведения см. в разделе PackageReference.

Когда проект верхнего уровня является пакетом, вы также можете контролировать этот поток с помощью атрибутов include и exclude с зависимостями, перечисленными в файле .nuspec. См. справочник по nuspec — зависимости.

Исключение ссылок

В некоторых сценариях сборки с одинаковым именем могут упоминаться в проекте несколько раз, что приводит к ошибкам на этапе проектирования и построения. Рассмотрим проект, содержащий пользовательскую версию C.dll, и ссылается на пакет C, который также содержит C.dll. В то же время проект также зависит от пакета B, который также зависит от пакета C и C.dll. В результате NuGet не может определить, какие C.dll использовать, но вы не можете просто удалить зависимость проекта от пакета C, так как пакет B также зависит от него.

Чтобы устранить эту проблему, необходимо напрямую ссылаться на нужный C.dll (или использовать другой пакет, ссылающийся на правильный), а затем добавить зависимость от пакета C, который исключает все его ресурсы. Это выполняется следующим образом в зависимости от используемого формата управления пакетами:

  • PackageReference: добавьте ExcludeAssets="All" в список зависимостей:

    <PackageReference Include="PackageC" Version="1.0.0" ExcludeAssets="All" />
    
  • packages.config: удалите ссылку на PackageC из файла .csproj, чтобы она ссылались только на нужную версию C.dll.

Обновления зависимостей во время установки пакета

Если версия зависимости уже удовлетворена, зависимость не обновляется во время других установок пакетов. Например, рассмотрим пакет A, который зависит от пакета B и указывает 1.0 для номера версии. Исходный репозиторий содержит версии 1.0, 1.1 и 1.2 пакета B. Если A установлен в проекте, который уже содержит B версии 1.0, то B 1.0 остается в использовании, так как он удовлетворяет ограничению версии. Однако, если пакет A требовал версию 1.1 или выше B, то будет установлен B 1.2.

Устранение несовместимых ошибок пакета

Во время операции восстановления пакета может появись сообщение об ошибке "Один или несколько пакетов несовместимы..." или что пакет "несовместим" с целевой платформой проекта.

Эта ошибка возникает, когда один или несколько пакетов, на которые ссылается в проекте, не указывают на то, что они поддерживают целевую платформу проекта; То есть пакет не содержит подходящую библиотеку DLL в папке lib для целевой платформы, совместимой с проектом. (См. целевые платформы для списка.)

Например, если проект нацелен на netstandard1.6 и вы пытаетесь установить пакет, содержащий DLL-файлы только в папках lib\net20 и \lib\net45, вы увидите следующие сообщения для пакета и, возможно, его зависимостей.

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'.

Чтобы устранить несовместимость, выполните одно из следующих действий:

  • Перенацелите проект на платформу, поддерживаемую пакетами, которые вы хотите использовать.
  • Обратитесь к автору пакетов и работайте с ним, чтобы добавить поддержку для выбранного фреймворка. На каждой странице описания пакетов nuget.org есть ссылка Связаться с владельцами для этой цели.

Совет

альтернативное решение: NuGetSolver — это расширение Visual Studio, разработанное Microsoft DevLabs, предназначенное для устранения конфликтов зависимостей. Он автоматизирует процесс выявления и решения этих проблем. Дополнительные сведения см. на странице NuGetSolver в Visual Studio Marketplace, и мы хотели бы услышать отзывы о вашем опыте.