ЗависимостиDependencies

Основным средством для добавления зависимостей в библиотеку .NET являются ссылки на пакеты NuGet.The primary way of adding dependencies to a .NET library is referencing NuGet packages. Ссылки на пакеты NuGet позволяют быстро подключить уже готовые функции, но для многих разработчиков .NET они часто становятся источником трудностей.NuGet package references allow you to quickly reuse and leverage already written functionality, but they're a common source of friction for .NET developers. Правильное управление зависимостями нужно для того, чтобы изменения в чужих библиотеках .NET не нарушали работу вашей библиотеки .NET, и наоборот.Correctly managing dependencies is important to prevent changes in other .NET libraries from breaking your .NET library, and vice versa!

Ромбовидные зависимостиDiamond dependencies

Широко распространены ситуации, когда в дереве зависимостей для проекта .NET существует несколько версий одного пакета.It's a common situation for a .NET project to have multiple versions of a package in its dependency tree. Например, если приложение зависит от двух пакетов NuGet, которые в свою очередь зависят от разных версий одного пакета.For example, an app depends on two NuGet packages, each of which depends on different versions of the same package. В схеме зависимостей это отображено в виде ромбовидной зависимости.A diamond dependency now exists in the app's dependency graph.

Ромбовидная зависимостьDiamond dependency

Во время сборки NuGet анализирует все пакеты, от которых зависит проект, в том числе зависимости зависимостей.At build time, NuGet analyzes all the packages that a project depends on, including the dependencies of dependencies. Если в этом списке есть нескольких версий одного пакета, из них выбирается одна.When multiple versions of a package are detected, rules are evaluated to pick one. Унификация пакетов очень важна, так как в .NET очень сложно реализовать параллельную работу нескольких версий одной сборки в одном приложении.Unifying packages is necessary because running side-by-side versions of an assembly in the same application is problematic in .NET.

Большинство ромбовидных зависимостей решаются очень легко, но в определенных обстоятельствах они могут создавать проблемы:Most diamond dependencies are easily resolved; however, they can create issues in certain circumstances:

  • Конфликт ссылок на пакет NuGet не позволяет разрешить конфликт версий при восстановлении пакетов.Conflicting NuGet package references prevent a version from being resolved during package restore.
  • Критические изменения между версиями приводят к ошибкам и исключениям во время выполнения.Breaking changes between the versions cause bugs and exceptions at run time.
  • Строгое именование пакета сборки , если изменилась версия сборки для приложения, выполняющегося на .NET Framework.The package assembly is strong named , the assembly version changed, and the app is running on .NET Framework. В такой ситуации требуется переадресация привязок.Assembly binding redirects are required.

Невозможно предсказать, какие пакеты будут работать параллельно с вашим.It's not possible to know what packages will be used alongside your own. Чтобы снизить вероятность проблем с библиотекой при возникновении ромбовидной зависимости, сократите до минимума число пакетов, от которых она зависит.A good way to reduce the likelihood of a diamond dependency breaking your library is to minimize the number of packages you depend on.

✔️ СЛЕДУЕТ проверить наличие ненужных зависимостей в библиотеке .NET.✔️ DO review your .NET library for unnecessary dependencies.

Диапазон версий для зависимостей NuGetNuGet dependency version ranges

Ссылка на пакет определяет диапазон допустимых пакетов.A package reference specifies the range of valid packages it allows. Как правило, ссылка в файле проекта определяет минимально необходимую версию пакета, а верхний предел отсутствует.Typically, the package reference version in the project file is the minimum version and there's no maximum.

<!-- Accepts any version 1.0 and above. -->
<PackageReference Include="ExamplePackage" Version="1.0" />

NuGet использует достаточно сложные правила при разрешении зависимостей, но по умолчанию пытается найти минимально приемлемую версию.The rules that NuGet uses when resolving dependencies are complex, but NuGet by default looks for the lowest applicable version. NuGet предпочитает минимально приемлемую версию, а не максимально доступную, чтобы снизить риск проблем с совместимостью.NuGet prefers the lowest applicable version over using the highest available because the lowest will have the least compatibility issues.

Это правило минимально приемлемых версий позволяет не указывать верхний предел для диапазона версий пакета, даже если вы хотите избежать получения последней версии.Because of NuGet's lowest applicable version rule, it isn't necessary to place an upper version or exact range on package references to avoid getting the latest version. NuGet и без этого будет искать наименьшую, то есть максимально совместимую, версию пакета.NuGet already tries to find the lowest, most compatible version for you.

<!-- Accepts 1.0 up to 1.x, but not 2.0 and higher. -->
<PackageReference Include="ExamplePackage" Version="[1.0,2.0)" />

<!-- Accepts exactly 1.0. -->
<PackageReference Include="ExamplePackage" Version="[1.0]" />

Наличие верхнего предела для версий пакета приведет к сбою NuGet в случае конфликта зависимостей.Upper version limits will cause NuGet to fail if there's a conflict. Предположим, одна из библиотек требует строго версию 1.0, а другая — версию не ниже 2.0.For example, one library accepts exactly 1.0 while another library requires 2.0 or above. Использование версии 2.0 может привести к ошибкам, если в ней внесены критические изменения. Но строгое верхнее ограничение гарантирует ошибку.While breaking changes may have been introduced in version 2.0, a strict or upper limit version dependency guarantees an error.

Конфликт ромбовидной зависимостиDiamond dependency conflict

❌ НЕ СЛЕДУЕТ использовать ссылки на пакет NuGet без минимально допустимой версии.❌ DO NOT have NuGet package references with no minimum version.

❌ НЕЖЕЛАТЕЛЬНО использовать ссылки на пакеты NuGet, требующие указания точной версии.❌ AVOID NuGet package references that demand an exact version.

❌ НЕЖЕЛАТЕЛЬНО использовать ссылки на пакет NuGet с указанием верхнего предела.❌ AVOID NuGet package references with a version upper limit.

Совместно используемые пакеты кода NuGetNuGet shared source packages

Чтобы снизить внешние зависимости пакета NuGet, можно использовать ссылки на совместно используемые пакеты кода.One way to reduce external NuGet package dependencies is to reference shared source packages. Совместно используемые пакеты кода содержат файлы исходного кода, которые включаются в проект по ссылке.A shared source package contains source code files that are included in a project when referenced. Так как эти файлы исходного кода напрямую включаются в проект и компилируются вместе с остальной частью проекта, не возникает никаких внешних зависимостей и возможностей для конфликта.Because you're just including source code files that are compiled with the rest of your project, there's no external dependency and chance of conflict.

Совместно используемые пакеты кода — это отличный способ включать небольшие фрагменты и функции.Shared source packages are great for including small pieces of functionality. Например, совместно используемый пакет кода может содержать вспомогательные методы для выполнения вызовов HTTP.For example, a shared source package of helper methods for making HTTP calls.

Совместно используемый исходный пакетShared source package

<PackageReference Include="Microsoft.Extensions.Buffers.Testing.Sources" PrivateAssets="All" Version="1.0" />

Совместно используемый исходный проектShared source project

Совместно используемые пакеты кода имеют ряд ограничений.Shared source packages have some limitations. Для них можно использовать только ссылки PackageReference, поэтому более старые проекты packages.config исключаются.They can only be referenced by PackageReference, so older packages.config projects are excluded. Также совместно используемые пакеты кода можно применять только в проектах с тем же типом языка.Also shared source packages are only usable by projects with the same language type. Из-за этих ограничений совместно используемые пакеты кода лучше всего подходят для совместного использования функций в рамках проекта с открытым исходным кодом.Because of these limitations shared source packages are best used to share functionality within an open-source project.

✔️ РЕКОМЕНДУЕТСЯ использовать ссылки на совместно используемые пакеты кода для небольших встроенных функций.✔️ CONSIDER referencing shared source packages for small, internal pieces of functionality.

✔️ РЕКОМЕНДУЕТСЯ создать совместно используемый пакет кода, если он предоставляет небольшие встроенные функции.✔️ CONSIDER making your package a shared source package if it provides small, internal pieces of functionality.

✔️ СЛЕДУЕТ указывать ссылку PrivateAssets="All" для совместно используемых пакетов кода.✔️ DO reference shared source packages with PrivateAssets="All".

Этот вариант сообщает NuGet, что пакет будет использоваться только во время разработки и не должен предоставляться как открытая зависимость.This setting tells NuGet the package is only to be used at development time and shouldn't be exposed as a public dependency.

❌ НЕ СЛЕДУЕТ использовать совместно используемые пакеты кода в общедоступном API.❌ DO NOT have shared source package types in your public API.

Совместно используемые типы компилируются прямо в сборку, которая содержит ссылку на них, и их невозможно передать в другую сборку.Shared source types are compiled into the referencing assembly and can't be exchanged across assembly boundaries. Например, совместно используемый тип IRepository в одном проекте не будет совпадать с тем же совместно используемым типом IRepository в другом проекте.For example, a shared-source IRepository type in one project is a separate type from the same shared-source IRepository in another project. Типы в совместно используемых пакетах кода должны иметь предел видимости internal.Types in shared source packages should have an internal visibility.

❌ НЕ СЛЕДУЕТ публиковать совместно используемые пакеты кода на сайте NuGet.org.❌ DO NOT publish shared source packages to NuGet.org.

Совместно используемые пакеты кода содержат исходный код и могут использоваться только в проектах с тем же типом языка.Shared source packages contain source code and can only be used by projects with the same language type. Например, совместно используемые пакеты кода на C# невозможно использовать в приложении F#.For example, a C# shared source package cannot be used by an F# application.

Опубликуйте совместно используемые пакеты кода в локальном веб-канале или MyGet для их использования в проекте внутренним образом.Publish shared source packages to a local feed or MyGet to consume them internally within your project.