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

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

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

Play ITЗагрузка интерактивного демо…


Пакеты​

Практический вход в тему​

Если статья кажется слишком теоретической, начните с минимального цикла:

dotnet add package Newtonsoft.Json
dotnet restore
dotnet list package --include-transitive

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


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 — неотъемлемой частью сборочного конвейера (см. примеры dotnet add package ниже). Таким образом, NuGet перестал быть "менеджером пакетов на стороне" и превратился в встроенную, неотделимую часть платформы.

dotnet add package

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

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

  1. Клиент — dotnet CLI (dotnet add package, dotnet remove package, dotnet list package) или интеграция в IDE (Visual Studio, Rider, VS Code). Клиент отвечает за взаимодействие с разработчиком, парсинг входных данных, модификацию файлов проекта и вызов движка разрешения зависимостей.
dotnet add package
  1. Механизм разрешения зависимостей (dependency resolver) — часть SDK, реализованная в NuGet.Commands и NuGet.DependencyResolver. Он анализирует граф зависимостей, находит совместимые версии пакетов с учётом ограничений (точные версии, диапазоны, исключения), проверяет конфликты и выбирает итоговое дерево зависимостей. Этот процесс называется restore.

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

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

dotnet restore

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 и выше) используется следующий алгоритм:

dotnet add package Newtonsoft.Json
  • Выбирается последняя стабильная (non-prerelease) версия, совместимая с целевой платформой проекта (Target Framework Moniker, TFM).
  • Если ни одной стабильной версии, поддерживающей TFM проекта, не существует — выбирается последняя стабильная версия вообще, даже если она требует более старого TFM (например, netstandard2.0), при условии, что текущий TFM совместим с ним (например, net8.0 → netstandard2.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 проекта. Если да — пакет считается применимым. Если нет — команда завершится ошибкой:

dotnet add package
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 restore

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

dotnet add package
  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 нужно скопировать в выходной каталог.
dotnet build

Файл 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.3 → 1.2.4 не должно ломать код потребителя.
  • MINOR — добавление функциональности в рамках текущего MAJOR-цикла. Допускается расширение API (новые классы, методы, перегрузки), но не удаление или изменение сигнатур существующих публичных элементов. Обновление 1.2.4 → 1.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 проигнорирует её.
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 или сборки.

dotnet add 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​

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).
dotnet restore
  • Поддерживает только один пакет за вызов. Для нескольких — вызывать последовательно или использовать 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 restore

dotnet remove package​

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

Синтаксис

dotnet remove [<PROJECT>] package <PACKAGE_NAME>

Особенности

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

Пример

dotnet remove package Newtonsoft.Json

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

dotnet list package​

dotnet list package

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

Синтаксис

dotnet list [<PROJECT>] package [options]

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

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

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

Код ITЗагрузка примера кода…

Важно: --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

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

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

Как включить по умолчанию (в .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 add package Newtonsoft.Json
  • dotnet list package --include-transitive — покажет, откуда взята версия (Central).
dotnet list package --include-transitive
  • dotnet restore — использует централизованные версии.
dotnet restore

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


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

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.
dotnet add package

Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.

Содержание