Перейти к основному содержимому

Пакеты и зависимости в .NET

Разработчику Архитектору

Пакеты

1. Понятие пакета в экосистеме .NET

В контексте языка C# и платформы .NET термин «пакет» (package) относится к распространяемой, самодостаточной единице программного обеспечения, содержащей одну или несколько сборок (assembly), метаданные, сопутствующие ресурсы (например, файлы локализации, конфигурации, схемы), а также дополнительную информацию, необходимую для корректного размещения, версионирования и интеграции с целевыми проектами. Пакет не является исходным кодом и не заменяет библиотеку как концепт — он выступает механизмом доставки и формализованным контейнером для кода и связанных с ним артефактов.

Особенность подхода .NET заключается в том, что пакеты интегрированы в жизненный цикл разработки на уровне инструментария (CLI, IDE) и конфигурационных файлов проекта, что обеспечивает воспроизводимость сборки и управляемость зависимостей. При этом сам пакет — это архив с расширением .nupkg, по сути ZIP-файл, структурированный в соответствии со спецификацией NuGet. Внутри такого архива находятся:

  • файл манифеста *.nuspec, описывающий метаданные пакета (имя, версия, авторы, описание, лицензия, зависимости);
  • каталоги lib/, содержащие скомпилированные сборки, сгруппированные по целевым платформам (например, net6.0, netstandard2.0);
  • каталоги content/, build/, tools/ и другие — для не-IL-артефактов (статические файлы, MSBuild-цели, скрипты инициализации);
  • при наличии — ресурсы локализации, документация в формате XML или Markdown, файлы символ отладки (.pdb), исходные карты (.snupkg-файл отдельно для символов).

Таким образом, пакет — это доставочный формат, а не выполняемая сущность. Он существует вне времени выполнения приложения и проявляется лишь на этапах проектирования, сборки и публикации.

2. Отличие пакета от сборки и от библиотеки

Эти три понятия часто смешиваются, хотя каждое из них описывает разные уровни абстракции:

  • Сборка (assembly) — это физический файл (.dll или .exe), содержащий IL-код, метаданные и ресурсы, который загружается средой выполнения .NET (CLR/CoreCLR). Сборка является минимальной единицей развертывания и изоляции; она может быть использована напрямую через ссылку в проекте (<Reference Include="..." />), но не предоставляет метаинформацию о совместимости, зависимости или авторстве.

  • Библиотека (library) — это логическая сущность: набор классов, интерфейсов, методов, предназначенный для повторного использования. Библиотека может быть реализована в одной или нескольких сборках; она определяется архитектурно, а не инфраструктурно.

  • Пакет (package) — это средство распространения библиотеки. Он может содержать одну или несколько сборок, а также всё, что необходимо, чтобы эта библиотека корректно встроилась в чужой проект: зависимости от других пакетов, инструкции по интеграции на уровне MSBuild, требования к версии .NET, дополнительные файлы (например, встраиваемые шаблоны или конфигурационные файлы по умолчанию).

Важный нюанс: не все сборки поставляются через пакеты. Например, сборки из Global Assembly Cache (GAC) в .NET Framework или референсные сборки из пакетов SDK (.NET SDK reference assemblies) подключаются иначе — без использования NuGet. Однако в современной практике .NET (начиная с .NET Core 1.0 и особенно с .NET 5+) пакет стал основным и предпочтительным способом распространения стороннего кода.

3. Исторический контекст и переход к NuGet

До появления NuGet разработчики C# сталкивались с так называемой «DLL-адской кухней» (DLL Hell): необходимость вручную копировать .dll-файлы, отслеживать версии, контролировать совместимость, решать конфликты имён и версий. Управление такими зависимостями масштабировалось плохо — даже в пределах одной команды при обновлении общей библиотеки требовалось вручную уведомлять всех потребителей, проверять сборку, тестировать регрессии.

Первые попытки централизации были связаны с GAC в .NET Framework, однако этот механизм требовал административных привилегий, не поддерживал side-by-side-версии (несколько версий одной сборки одновременно в одном процессе) и не решал задачу распространения внешних библиотек (например, open-source). Также широко использовались кастомные решения: shared SVN-репозитории с бинарниками, архивы библиотек в корпоративных хранилищах, MSBuild-скрипты для подгрузки зависимостей.

NuGet, изначально разработанный как open-source-проект (первый релиз — 2010 год), изменил парадигму: он ввёл декларативное управление зависимостями. Теперь разработчик не копирует бинарники вручную, а указывает в файле проекта намерение использовать некую библиотеку — с точным именем и желаемой версией. Инструментарий (CLI или IDE) автоматически находит, загружает, проверяет подлинность (при включённой подписи пакетов), размещает в локальном кэше и подключает зависимости — включая транзитивные (зависимости зависимостей). Это позволило сделать воспроизводимость сборки стандартом.

С переходом на .NET Core и формат проектов на основе SDK (<Project Sdk="Microsoft.NET.Sdk">) интеграция NuGet стала ещё глубже: файл .csproj сам по себе стал описанием зависимостей, а команда dotnet restore — неотъемлемой частью сборочного конвейера. Таким образом, NuGet перестал быть «менеджером пакетов на стороне» и превратился в встроенную, неотделимую часть платформы.

4. Управление пакетами: принципы и архитектура

Управление пакетами в .NET строится на трёх ключевых компонентах:

  1. Клиентdotnet CLI (dotnet add package, dotnet remove package, dotnet list package) или интеграция в IDE (Visual Studio, Rider, VS Code). Клиент отвечает за взаимодействие с разработчиком, парсинг входных данных, модификацию файлов проекта и вызов движка разрешения зависимостей.

  2. Механизм разрешения зависимостей (dependency resolver) — часть SDK, реализованная в NuGet.Commands и NuGet.DependencyResolver. Он анализирует граф зависимостей, находит совместимые версии пакетов с учётом ограничений (точные версии, диапазоны, исключения), проверяет конфликты и выбирает итоговое дерево зависимостей. Этот процесс называется restore.

  3. Репозитории пакетов (sources) — HTTP- или файловые источники, где хранятся .nupkg-файлы. По умолчанию используется публичный репозиторий nuget.org, но корпоративные среды почти всегда разворачивают приватные источники (например, Azure Artifacts, Nexus Repository, ProGet). Источники могут быть иерархическими: клиент последовательно опрашивает все подключённые репозитории, пока не найдёт подходящую версию пакета.

Важно понимать, что установка пакета — это не копирование файлов в проект. Это изменение описания проекта (<PackageReference> в .csproj), после чего при следующем dotnet restore происходит загрузка пакета во глобальный кэш (%userprofile%\.nuget\packages на Windows, ~/.nuget/packages на Unix-системах) и создание символических ссылок или копий в выходной каталог при сборке. Такой подход экономит место на диске (одна версия пакета — один раз в кэше), ускоряет повторные сборки и изолирует проекты друг от друга.


5. Детальный разбор команды dotnet add package

Команда

dotnet add package <Package.Name> [--version <Version>]

представляет собой высокоуровневый интерфейс к системе управления зависимостями .NET. Она не загружает пакет немедленно и не модифицирует бинарные артефакты. Вместо этого она:

  1. Парсит входные параметры — проверяет корректность имени пакета (должно соответствовать формату идентификатора NuGet: ASCII-буквы, цифры, точки и дефисы, без пробелов, длина до 128 символов), версии (если указана), флагов.
  2. Определяет целевой проект — если в текущем каталоге находится ровно один .csproj-файл, он выбирается автоматически; иначе требуется явное указание (-p <file> или --project <file>).
  3. Выполняет поиск подходящей версии пакета — обращается к сконфигурированным источникам (по умолчанию — nuget.org), считывает метаданные пакета, фильтрует доступные версии по заданному условию.
  4. Формирует запись <PackageReference> и вставляет её в .csproj-файл в соответствующую группу ItemGroup.

5.1. Что происходит при вызове без указания версии?

Если версия не указана (например, dotnet add package Newtonsoft.Json), поведение определяется политикой разрешения по умолчанию. На момент 2025 года (SDK .NET 8 и выше) используется следующий алгоритм:

  • Выбирается последняя стабильная (non-prerelease) версия, совместимая с целевой платформой проекта (Target Framework Moniker, TFM).
  • Если ни одной стабильной версии, поддерживающей TFM проекта, не существует — выбирается последняя стабильная версия вообще, даже если она требует более старого TFM (например, netstandard2.0), при условии, что текущий TFM совместим с ним (например, net8.0netstandard2.0 — допустимо).
  • Prerelease-версии (помеченные суффиксом -alpha, -beta, -rc) никогда не выбираются, если явно не указан флаг --prerelease.

Это поведение можно изменить через конфигурацию NuGet (файл nuget.config), но по умолчанию система стремится к максимальной стабильности и воспроизводимости.

Примечание: «Последняя стабильная версия» определяется по версионному номеру в соответствии с правилами семантического версионирования (SemVer 2.0), а не по дате публикации. Например, 13.0.1 новее 12.0.300, даже если опубликована позже.

5.2. Влияние Target Framework Moniker (TFM)

TFM — это строка вида net8.0, net6.0-windows, netstandard2.1, указываемая в .csproj в элементе <TargetFramework> (или <TargetFrameworks> для нескольких). Он определяет минимальный контракт среды выполнения, который должен быть обеспечен для успешной компиляции и запуска.

Пакет, в свою очередь, декларирует, какие TFM он поддерживает, через наличие сборок в подкаталогах lib/ внутри .nupkg. Например:

lib/
├── net461/
│ └── Newtonsoft.Json.dll ← для .NET Framework 4.6.1+
├── netstandard2.0/
│ └── Newtonsoft.Json.dll ← универсальная сборка
└── net6.0/
└── Newtonsoft.Json.dll ← оптимизированная под .NET 6+

При dotnet add package клиент проверяет, есть ли в пакете сборка, совместимая с TFM проекта. Если да — пакет считается применимым. Если нет — команда завершится ошибкой:

NU1202: Package Newtonsoft.Json 13.0.1 is not compatible with net8.0-windows7.0 (.NETCoreApp,Version=v8.0).

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

5.3. Явное указание версии: синтаксис и интерпретация

Версия может быть задана несколькими способами:

СинтаксисИнтерпретация
13.0.1Точная версия. Разрешается только эта конкретная версия — ни выше, ни ниже.
13.0.*Любой патч-релиз внутри минорной версии 13.0 (13.0.0, 13.0.1, …, 13.0.999). Не соответствует SemVer-диапазонам, используется редко.
[13.0.0, 14.0.0)Версии 13.0.0 и <14.0.0 — то есть любая 13.x.y, кроме prerelease, если не указан --prerelease.
*«Любая стабильная версия». Эквивалентно отсутствию --version, но делает намерение явным.

Внутри .csproj запись может выглядеть так:

<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<!-- или -->
<PackageReference Include="Newtonsoft.Json">
<Version>[13.0.0, 14.0.0)</Version>
</PackageReference>

Обратите внимание: значение Version не обязательно должно быть строкой — оно может быть вынесено в свойство (см. раздел о централизованном управлении), но семантика остаётся неизменной.


6. Источники пакетов: конфигурация и приоритеты

По умолчанию SDK использует публичный репозиторий nuget.org. Однако в реальных проектах почти всегда задействуются дополнительные источники:

  • Корпоративные репозитории (Azure Artifacts, JFrog Artifactory, Nexus, ProGet) — для внутренних библиотек, закрытых зависимостей, форков open-source с кастомными патчами.
  • Локальные источники (file://) — для отладки собственных пакетов перед публикацией.
  • Репозитории CI-систем — временные фиды для сборок веток или PR.

Источники настраиваются в файле nuget.config, который может находиться:

  • глобально — %appdata%\NuGet\nuget.config (Windows) или ~/.nuget/NuGet/NuGet.Config (Linux/macOS);
  • на уровне решения — рядом с .sln;
  • на уровне проекта — рядом с .csproj.

Приоритет источников определяется:

  1. Порядком объявления в nuget.config (первый подходящий выигрывает);
  2. Флагом clear в элементе <packageSources> — сбрасывает все ранее определённые источники.

Пример конфигурации:

<configuration>
<packageSources>
<clear />
<add key="internal" value="https://pkgs.dev.azure.com/contoso/_packaging/internal/nuget/v3/index.json" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>

При поиске пакета клиент опрашивает источники последовательно, пока не найдёт первую версию, удовлетворяющую условиям. Это критично для случаев, когда один и тот же пакет существует и в nuget.org, и во внутреннем репозитории — приоритет будет у первого в списке.

Важно: NuGet поддерживает как v2, так и v3 API репозиториев, но v3 (на базе HTTP/JSON) рекомендуется: он быстрее, масштабируемее и поддерживает продвинутые сценарии (например, поиск по тегам, фильтрация по TFM).


7. Процесс dotnet restore: что происходит «под капотом»

Команда dotnet add package лишь подготавливает описание зависимости. Фактическая загрузка пакета происходит при следующем dotnet restore (или автоматически при сборке, если не отключено). Этот этап включает:

  1. Построение графа зависимостей — рекурсивный обход всех <PackageReference> в проекте и его транзитивных зависимостях.
  2. Разрешение конфликтов версий — если два пакета требуют разные версии одной и той же зависимости (например, A → X v1.0, B → X v2.0), механизм пытается найти единственную версию, совместимую со всеми требованиями. Обычно выбирается самая высокая из заявленных, если она удовлетворяет всем ограничениям.
  3. Проверка совместимости TFM — для каждой зависимости проверяется, поддерживает ли она целевой фреймворк проекта.
  4. Загрузка пакетов в глобальный кэш.nupkg скачиваются, проверяются (при включённой проверке подписи), распаковываются в ~/.nuget/packages/<id>/<version>.
  5. Генерация project.assets.json — ключевой файл в obj/, содержащий итоговое дерево зависимостей, пути к сборкам, метаданные для компилятора и MSBuild. Именно на его основе при сборке (dotnet build) определяются, какие .dll нужно скопировать в выходной каталог.

Файл project.assets.json — это «снимок» состояния зависимостей на момент restore. Его наличие позволяет избежать повторного restore при последующих сборках, если .csproj и nuget.config не менялись.


8. Семантическое версионирование (SemVer) в экосистеме NuGet

С 2015 года NuGet официально поддерживает спецификацию Semantic Versioning 2.0.0. Это не просто соглашение — это формальная основа, на которой строится разрешение зависимостей. Версия пакета имеет следующую каноническую форму:

MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]

где:

  • MAJOR, MINOR, PATCH — неотрицательные целые числа;
  • PRERELEASE — необязательный суффикс (например, alpha, beta.2, rc1), разделяемый дефисом;
  • BUILD — необязательные метаданные сборки (например, +20251118.1), разделяемые плюсом.

8.1. Семантический смысл компонентов

  • PATCH — исправления ошибок, не затрагивающие публичный API. Совместимость всегда сохраняется. Обновление с 1.2.31.2.4 не должно ломать код потребителя.
  • MINOR — добавление функциональности в рамках текущего MAJOR-цикла. Допускается расширение API (новые классы, методы, перегрузки), но не удаление или изменение сигнатур существующих публичных элементов. Обновление 1.2.41.3.0 безопасно для уже написанного кода.
  • MAJOR — обратно несовместимые изменения: удаление классов/методов, изменение сигнатур, изменение поведения без изменения сигнатур. Переход на новую MAJOR-версию требует ручного рефакторинга.

Ключевой принцип: если пакет A зависит от пакета B версии ^1.2.3 (см. ниже), а B выпускает 1.3.0, то A по-прежнему совместим с новой версией — и это гарантируется SemVer. Если же B выпускает 2.0.0, то совместимость не гарантируется, и потребитель должен явно принять решение об обновлении.

8.2. Диапазоны версий и их интерпретация

NuGet поддерживает два основных стиля указания диапазонов:

8.2.1. Традиционный синтаксис (основан на [, ], (, ))
  • [1.0.0] — точно 1.0.0
  • [1.0.0, ) — ≥ 1.0.0 (все стабильные версии от 1.0.0 и выше)
  • [1.0.0, 2.0.0) — ≥ 1.0.0 и < 2.0.0
  • (, 1.0.0] — ≤ 1.0.0

Скобки [ и ] означают включительно, круглые ( и )исключительно.

8.2.2. Caret-нотация (^) — «совместимое обновление»

Это сокращённая форма, введённая в NuGet 4.3+ и активно используемая в современных проектах:

ЗаписьЭквивалентПояснение
^1.2.3[1.2.3, 2.0.0)Любая версия ≥1.2.3, до следующей MAJOR
^1.2[1.2.0, 2.0.0)Минорная версия фиксирована как минимум 1.2
^1[1.0.0, 2.0.0)Любая 1.x.y
^0.2.3[0.2.3, 0.3.0)Особый случай: 0.x считается «нестабильным», поэтому ^ ограничивает MINOR, а не MAJOR

Почему 0.x — особый случай? По SemVer, версии с MAJOR = 0 находятся в «режиме активной разработки», и даже MINOR-изменения могут быть несовместимыми. Поэтому ^0.2.3 не разрешает 0.3.0 — только 0.2.4, 0.2.5 и т. д.

Caret-нотация — рекомендуемый способ указания зависимостей в .csproj, так как она обеспечивает баланс между стабильностью и актуальностью: вы получаете исправления и новые функции, но защищены от ломающих изменений.

8.3. Prerelease-версии: когда и как их использовать

Prerelease-версии:

  • Не участвуют в разрешении по умолчанию — даже если это «последняя» версия, dotnet add package проигнорирует её.
  • Подключаются только при явном указании версии (--version 2.0.0-rc1) или флага --prerelease.
  • Имеют меньший приоритет при сравнении: 1.0.0-alpha < 1.0.0.

Prerelease-метки сортируются лексикографически после разбиения по точкам и дефисам. Например:

1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta < 1.0.0-rc.1 < 1.0.0

Это позволяет публиковать последовательные предварительные релизы и гарантировать их упорядоченность.

Практическое правило: prerelease-версии допустимы в экспериментальных ветках, CI-сборках, внутреннем тестировании. В production-ветках и релизных сборках они должны отсутствовать — во избежание нестабильности и неопределённости времени жизни API.


9. Транзитивные зависимости: граф, разрешение и риски

Если проект P зависит от пакета A, а A — от B, то B становится транзитивной зависимостью P. Это мощный механизм повторного использования, но и источник основных сложностей.

9.1. Принцип dependency flattening

NuGet применяет стратегию сглаживания графа (flattening): вместо вложения зависимостей (A → B → C, P → A → B → C) строится плоское дерево, где все пакеты поднимаются на один уровень — при условии совместимости версий.

Пример:

  • P зависит от A v2.0 и B v1.1
  • A зависит от C v1.0
  • B зависит от C v1.2

Результат: P использует C v1.2 — самая высокая версия, удовлетворяющая обоим требованиям (A требует ≥1.0, B — ≥1.2). Это безопасно, так как SemVer гарантирует, что 1.2 совместима с 1.0.

9.2. Конфликты версий и downgrade-предупреждения

Конфликт возникает, когда требования несовместимы. Например:

  • A требует C ≥ 2.0
  • B требует C ≥ 1.0 и < 2.0 (например, [1.0.0, 2.0.0))

В этом случае NuGet не может найти единую версию C, удовлетворяющую обоим условиям. Поведение:

  1. По умолчанию — ошибка восстановления (NU1107: Version conflict detected).
  2. Если в .csproj явно указан <PackageReference Include="C" Version="1.5.0" />, то выбирается эта версия, но выдаётся предупреждение NU1605: Detected package downgrade.

Предупреждение NU1605 означает: «Вы просите использовать более старую версию, чем требует одна из зависимостей. Это может привести к runtime-ошибкам». Например, если A скомпилирован против C v2.0 и вызывает метод C.Foo(), которого нет в C v1.5.0 — приложение упадёт с MissingMethodException.

Важно: в .NET Core и .NET 5+ нет механизма binding redirects (как в .NET Framework), который позволял бы «перенаправлять» вызовы со старой версии на новую. Поэтому downgrade — это реальный риск, а не теоретическая угроза.

9.3. Диагностика транзитивных зависимостей

Ключевые команды для анализа:

# Список прямых зависимостей
dotnet list package

# Список ВСЕХ зависимостей (включая транзитивные)
dotnet list package --include-transitive

# Древовидное представление графа
dotnet list package --include-transitive --verbosity detailed

# Проверка обновлений (с указанием, какие версии доступны)
dotnet list package --outdated

Результат --include-transitive особенно ценен: он показывает, откуда пришла та или иная зависимость. Например:

Project 'MyApp'
Newtonsoft.Json (13.0.1)
System.Text.Json (4.7.2) <-- транзитивная, от Newtonsoft.Json
Serilog (3.1.1)
System.Text.Json (6.0.0) <-- транзитивная, от Serilog

Это сигнал: две разные версии System.Text.Json в одном проекте. NuGet выберет 6.0.0, но если Newtonsoft.Json не протестирован с ней — возможны проблемы.


10. Практические стратегии управления транзитивными зависимостями

10.1. Явное фиксирование «проблемных» пакетов

Если вы знаете, что определённая сборка (например, System.Text.Json, Microsoft.Extensions.*) часто вызывает конфликты, можно явно указать её в корне проекта:

<PackageReference Include="System.Text.Json" Version="8.0.5" />

Это гарантирует, что все потребители получат одну и ту же версию — даже если транзитивные зависимости требуют ниже.

10.2. Использование <NoWarn>NU1605</NoWarn> — с осторожностью

Отключение предупреждения не решает проблему — оно лишь скрывает симптом. Делать это допустимо, только если:

  • Вы точно знаете, что более старая версия API-совместима с требованиями транзитивных потребителей;
  • Вы провели тестирование на всех сценариях использования;
  • Имеется документированное обоснование.

В противном случае — это технический долг.

10.3. Dependency trimming и анализ сборки

Начиная с .NET 6, SDK включает механизм link-time analysis (при <PublishTrimmed>true</PublishTrimmed>), который удаляет неиспользуемый код из зависимостей. Он также может выявлять несовместимости на этапе публикации: если транзитивная зависимость вызывает отсутствующий метод — ошибка будет обнаружена до развёртывания.


PMC

Обратите внимание:

  • PMC — это PowerShell-хост, встроенный в Visual Studio, а не терминал dotnet CLI. Он использует CmdLets из модуля NuGet.PackageManagement.PowerShellCmdlets.
  • Команды PMC работают только внутри Visual Studio (не в cmd, bash, pwsh вне IDE).
  • PMC оперирует понятиями solution и default project (выбранного в выпадающем списке над консолью).
  • В современной практике (начиная с .NET Core) dotnet CLI предпочтительнее, но PMC остаётся востребованным в enterprise-средах с .NET Framework, сложными multi-project solutions и сценариями, требующими тонкой настройки.

Справочник команд консоли диспетчера пакетов (Package Manager Console)

1. Установка и обновление пакетов

Install-Package

Устанавливает пакет в проект (и, при необходимости, его зависимости).

Синтаксис

Install-Package [-Id] <string> [-Version <string>] [-ProjectName <string>] [-IgnoreDependencies] [-Force] [-Source <string>] [-IncludePrerelease]

Пояснение параметров

  • -Id — имя пакета (обязательный, можно опускать имя параметра: Install-Package Newtonsoft.Json).
  • -Version — точная версия или диапазон (например, "13.0.1", "[13.0.0, 14.0.0)"). Если не указано — выбирается последняя стабильная.
  • -ProjectName — имя проекта в решении (по умолчанию — Default project из выпадающего списка).
  • -IgnoreDependencies — не устанавливать транзитивные зависимости. Опасно: может привести к неработоспособности.
  • -Force — переустановить пакет даже при отсутствии изменений; обойти проверку совместимости (не рекомендуется).
  • -Source — URL или имя зарегистрированного источника (например, "nuget.org", "https://pkgs.dev.azure.com/...").
  • -IncludePrerelease — разрешить prerelease-версии.

Примеры

# Установить последнюю стабильную версию в default project
Install-Package Newtonsoft.Json

# Установить конкретную версию в указанный проект
Install-Package Serilog -Version 3.1.1 -ProjectName "MyApp.Logging"

# Установить prerelease из внутреннего источника
Install-Package Contoso.Data -IncludePrerelease -Source "internal"

Особенность: в отличие от dotnet add package, команда Install-Package сразу выполняет restore — изменения вступают в силу немедленно, без отдельного вызова Update-Package или сборки.


Update-Package

Обновляет пакеты до новых версий в проектах решения.

Синтаксис

Update-Package [[-Id] <string>] [-Version <string>] [-ProjectName <string>] [-Safe] [-ToHighestMinor] [-ToHighestPatch] [-Source <string>] [-Reinstall] [-IncludePrerelease]

Варианты использования

  • Без параметров: обновляет все пакеты во всех проектах решения до последних стабильных версий.
  • С -Id: обновляет только указанный пакет.
  • С -ProjectName: ограничивает обновление одним проектом.

Специальные флаги

  • -Safe — обновляет только до ближайшей совместимой версии (не выше текущего MAJOR; эквивалент ^ в SemVer).
  • -ToHighestMinor — обновляет до последней MINOR в рамках текущего MAJOR (например, 1.2.3 → 1.9.0, но не 2.0.0).
  • -ToHighestPatch — только патч-версии (1.2.3 → 1.2.99).
  • -Reinstall — переустанавливает пакет без изменения версии (полезно при повреждении кэша или изменении TFM проекта).

Примеры

# Безопасное обновление одного пакета (только патчи и миноры в рамках MAJOR)
Update-Package Newtonsoft.Json -Safe

# Обновить все пакеты в проекте до последних патчей
Update-Package -ProjectName "Core" -ToHighestPatch

# Переустановить все пакеты после смены TargetFramework
Update-Package -Reinstall

Важно: Update-Package не обновляет PackageReference, если версия уже соответствует запрошенному диапазону (например, Version="13.0.1" не станет 13.0.2, если в .csproj явно зафиксировано 13.0.1). Для «разморозки» версии требуется сначала Uninstall-Package, затем Install-Package с диапазоном.


2. Удаление пакетов

Uninstall-Package

Удаляет пакет из проекта.

Синтаксис

Uninstall-Package [-Id] <string> [-ProjectName <string>] [-Force]

Особенности

  • Удаляет только прямую зависимость. Транзитивные зависимости остаются, если на них ссылаются другие пакеты.
  • Если пакет используется в коде — после удаления возможны ошибки компиляции (но PMC не проверяет это автоматически).
  • -Force подавляет запросы подтверждения и игнорирует зависимости (опасно: может оставить «висячие» ссылки).

Пример

Uninstall-Package Newtonsoft.Json -ProjectName "LegacyModule"

3. Анализ и диагностика

Get-Package

Выводит список установленных пакетов.

Синтаксис

Get-Package [[-Id] <string>] [-ProjectName <string>] [-ListAvailable] [-Source <string>] [-IncludePrerelease] [-AllVersions]

Режимы работы

  • Без параметров: список установленных пакетов в default project.
  • -ListAvailable: список доступных пакетов в источнике (аналог поиска в UI NuGet).
  • -AllVersions: показать все версии пакета (при -ListAvailable).
  • -ProjectName: ограничить вывод конкретным проектом.

Примеры

# Список пакетов в проекте
Get-Package -ProjectName "WebApi"

# Найти все доступные версии пакета
Get-Package EntityFramework -ListAvailable -AllVersions -Source nuget.org

Get-Project

Выводит информацию о проектах в решении.

Полезно для скриптов:

# Список всех проектов
Get-Project

# Пути к файлам проектов
Get-Project | Select-Object Name, FullName

4. Работа с источниками (sources)

Get-PackageSource, Register-PackageSource, Unregister-PackageSource

Управление репозиториями на уровне пользователя (глобально, не в nuget.config решения).

Примеры

# Показать все источники
Get-PackageSource

# Добавить внутренний источник (с приоритетом 1)
Register-PackageSource -Name "internal" -Location "https://pkgs.dev.azure.com/contoso/..." -Trusted -Priority 1

# Удалить источник
Unregister-PackageSource -Name "old-feed"

Примечание: изменения через PMC затрагивают глобальный NuGet.Config, а не локальный в решении. Для командной работы предпочтительнее использовать nuget.config в корне решения.


5. Продвинутые сценарии

Sync-Package

Синхронизирует версию пакета между проектами.

Синтаксис

Sync-Package [-Id] <string> [-Version <string>] [-ProjectName <string>] [-IgnoreDependencies] [-Source <string>]

Применение
Если в решении несколько проектов используют один пакет, но с разными версиями — Sync-Package приводит их к единой версии (указанной или последней общей).

# Привести все проекты к одной версии Newtonsoft.Json
Sync-Package Newtonsoft.Json

Эта команда особенно ценна в mono-repo с shared infrastructure.


Add-BindingRedirect

Только для .NET Framework. Генерирует <bindingRedirect> в app.config/web.config для разрешения конфликтов версий сборок в GAC.

Add-BindingRedirect -ProjectName "LegacyApp"

В .NET Core / .NET 5+ эта команда не работает и не нужна — механизм binding redirects отсутствует.


6. Ограничения и особенности PMC

  • Только для PackageReference и packages.config. PMC корректно работает с обоими форматами управления зависимостями, но поведение отличается:
    • В packages.config пакеты копируются в папку packages/ решения.
    • В PackageReference (современный формат) — используется глобальный кэш, как в dotnet CLI.
  • Нет поддержки централизованных версий (Directory.Packages.props). PMC не распознаёт <CentralPackageVersion> и будет пытаться установить версию напрямую в .csproj, что может нарушить единообразие.
  • Автодополнение: в PMC доступно Tab-дополнение имён пакетов и проектов.

dotnet CLI

В отличие от Package Manager Console, dotnet CLI — это кроссплатформенный, консольный, идемпотентный инструмент, предназначенный для интеграции в CI/CD, скрипты и повседневную работу вне IDE. Он работает только с форматом PackageReference, не поддерживает packages.config, и является каноническим способом управления зависимостями в экосистеме .NET.


Справочник команд dotnet CLI для управления пакетами

1. Установка и удаление пакетов

dotnet add package

Добавляет <PackageReference> в .csproj и инициирует восстановление зависимости.

Синтаксис

dotnet add [<PROJECT>] package <PACKAGE_NAME> [options]

Основные опции

ОпцияНазначение
--version <VERSION>Точная версия или диапазон (например, "13.0.1", "[13.0.0, 14.0.0)", "*"). Без неё — последняя стабильная.
--prereleaseРазрешить prerelease-версии (например, 2.0.0-rc.1).
--package-directory <DIR>Устаревшее. Использовалось для packages.config; игнорируется в SDK-проектах.
--source <SOURCE>URL или имя источника (должен быть зарегистрирован в nuget.config или передан как --configfile).
--interactiveВключить интерактивный ввод (например, для аутентификации в приватном репозитории).

Особенности поведения

  • Если <PROJECT> не указан — используется первый .csproj в текущем каталоге (или ошибка, если их несколько/нет).
  • Команда не скачивает пакет немедленно, а только изменяет .csproj. Загрузка происходит при следующем dotnet restore (или автоматически при dotnet build).
  • Поддерживает только один пакет за вызов. Для нескольких — вызывать последовательно или использовать Directory.Packages.props (см. ниже).

Примеры

# Добавить последнюю стабильную версию
dotnet add package Newtonsoft.Json

# Добавить конкретную версию
dotnet add package Serilog.AspNetCore --version 8.0.1

# Добавить prerelease из альтернативного источника
dotnet add package Contoso.SDK --prerelease --source "https://pkgs.dev.azure.com/contoso/nuget/v3/index.json"

Примечание: при указании --version "13.0.0" в .csproj будет записано <Version>13.0.0</Version>. При --version "[13.0.0, 14.0.0)"<Version>[13.0.0, 14.0.0)</Version>. Это влияет на дальнейшее поведение dotnet restore.


dotnet remove package

Удаляет <PackageReference> из .csproj.

Синтаксис

dotnet remove [<PROJECT>] package <PACKAGE_NAME>

Особенности

  • Удаляется только прямая зависимость. Транзитивные зависимости, которые больше никем не используются, будут исключены при следующем dotnet restore, но не немедленно.
  • Если пакет используется в коде — ошибки появятся только при компиляции.

Пример

dotnet remove package Newtonsoft.Json

2. Анализ и диагностика зависимостей

dotnet list package

Выводит дерево зависимостей проекта.

Синтаксис

dotnet list [<PROJECT>] package [options]

Ключевые опции

ОпцияЭффект
--include-transitiveПоказать все зависимости, включая транзитивные.
--outdatedПоказать только пакеты, для которых доступны обновления.
--deprecatedПоказать устаревшие (deprecated) пакеты.
--vulnerableПоказать пакеты с известными уязвимостями (требует интеграции с базами CVE, например, через dotnet nuget verify).
--framework <TFM>Ограничить вывод для конкретного Target Framework (при multi-targeting).

Примеры вывода и применения

# Прямые зависимости
dotnet list package
# Project 'MyApp' has the following package references
# [net8.0]:
# Top-level Package Requested Resolved
# > Newtonsoft.Json 13.0.1 13.0.1
# > Serilog 3.1.1 3.1.1

# Полное дерево с транзитивными
dotnet list package --include-transitive
# Top-level Package Requested Resolved
# > Serilog 3.1.1 3.1.1
# └── System.Text.Json 6.0.0 6.0.0 <-- транзитивная

# Пакеты, которые можно безопасно обновить (только патчи/миноры)
dotnet list package --outdated --vulnerable

Важно: --outdated учитывает текущую политику версионирования в .csproj. Если стоит Version="13.0.1", то 13.0.2 покажется как outdated; если Version="[13.0.0, 14.0.0)" — обновление до 13.0.2 не предложит, так как оно уже разрешено.


dotnet list reference

Показывает проектные ссылки (<ProjectReference>), а не пакеты. Полезно для отличия внутренних зависимостей от внешних.

dotnet list reference
# Project references for 'MyApp':
# MyApp.Core
# MyApp.Infrastructure

3. Восстановление и синхронизация

dotnet restore

Скачивает пакеты и формирует obj/project.assets.json.

Синтаксис

dotnet restore [<PROJECT>|<SOLUTION>] [options]

Ключевые опции

ОпцияНазначение
--source <SOURCE>Временно добавить источник (не изменяет nuget.config).
--configfile <FILE>Использовать альтернативный nuget.config.
--disable-parallelОтключить параллельную загрузку (полезно при нестабильном интернете).
--forceИгнорировать кэш и перезагрузить все пакеты.
--locked-modeРаботать только с зафиксированными версиями из project.assets.json.lock (см. ниже).

Особенности

  • Выполняется автоматически при dotnet build, если файл project.assets.json отсутствует или устарел.
  • Можно вызывать на уровне решения (.sln):
    dotnet restore MySolution.sln

dotnet restore --use-lock-file и --locked-mode

Современный механизм гарантированной воспроизводимости сборки.

  • dotnet restore --use-lock-file создаёт packages.lock.json — файл, фиксирующий точные версии всех зависимостей (включая транзитивные).
  • dotnet restore --locked-mode требует, чтобы текущее дерево зависимостей в точности совпадало с packages.lock.json. Если кто-то обновил пакет локально — сборка упадёт.

Как включить по умолчанию.csproj):

<PropertyGroup>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
</PropertyGroup>

Рекомендация: использовать packages.lock.json в production-сборках и CI/CD — это предотвращает «сдвиг зависимостей» при обновлении nuget.org.


4. Работа с источниками (без редактирования nuget.config)

dotnet nuget list source

Показывает зарегистрированные источники (аналог nuget sources list).

dotnet nuget list source
# Registered Sources:
# 1. nuget.org [Enabled]
# https://api.nuget.org/v3/index.json
# 2. internal [Enabled]
# https://pkgs.dev.azure.com/contoso/...

dotnet nuget add source, remove source, disable source, enable source

Управление источниками через CLI.

Пример добавления приватного источника с аутентификацией:

dotnet nuget add source "https://pkgs.dev.azure.com/contoso/_packaging/internal/nuget/v3/index.json" \
--name "internal" \
--username "timur@contoso.com" \
--password "pat-token" \
--store-password-in-clear-text

Безопасность: --store-password-in-clear-text сохраняет пароль в NuGet.Config в открытом виде. В CI лучше использовать переменные окружения (NUGET_CREDENTIALPROVIDER_SESSIONTOKENCACHE_ENABLED=1) или Azure Artifacts Credential Provider.


5. Продвинутые сценарии

Централизованное управление версиями (Central Package Management, CPM)

Начиная с .NET 6, поддерживается единый файл Directory.Packages.props в корне репозитория для фиксации версий.

Как включить (Directory.Packages.props):

<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Serilog" Version="3.1.1" />
</ItemGroup>
</Project>

Использование в проектах:

<!-- В .csproj достаточно указать имя -->
<PackageReference Include="Newtonsoft.Json" />
<!-- Версия берётся из Directory.Packages.props -->

Соответствующие команды CLI:

  • dotnet add package Newtonsoft.Json — добавит <PackageReference> без версии.
  • dotnet list package --include-transitive — покажет, откуда взята версия (Central).
  • dotnet restore — использует централизованные версии.

Преимущество: гарантирует единообразие версий в решении из 100+ проектов.
Ограничение: нельзя указать разные версии одного пакета в разных проектах без обходных путей (<PackageReference Update="..." Version="..." />).


dotnet pack и dotnet nuget push — кратко (для контекста)

Хотя создание пакетов — отдельная тема, упомянем ключевые команды:

КомандаНазначение
dotnet packСобирает .nupkg из проекта (требует <GeneratePackageOnBuild>true</GeneratePackageOnBuild> или вызова явно).
dotnet nuget push <FILE>.nupkg --source <SRC> --api-key <KEY>Публикует пакет в репозиторий.

6. Сравнение dotnet CLI и PMC: когда что использовать

Критерийdotnet CLIPackage Manager Console
ПлатформаКроссплатформенно (Windows, Linux, macOS)Только Windows + Visual Studio
CI/CDПолная поддержкаНе поддерживается
Формат проектаТолько PackageReferencePackageReference и packages.config
СкриптованиеИдеален (bash, PowerShell, Makefile)Ограничен (PowerShell внутри VS)
ИнтерактивностьМинимальнаяВысокая (автодополнение, UI-интеграция)
Центр. управление версиямиПолная поддержкаНе поддерживается

Рекомендация:

  • Для новых проектов — исключительно dotnet CLI.
  • Для поддержки legacy-решений на .NET Framework с packages.config — допустимо PMC.
  • В командной работе — фиксируйте зависимости через dotnet add package, а не через UI Visual Studio.

Освоение главы0%