Cómo Resuelve NuGet las dependencias del paquete
Cada vez que se instala o vuelve a instalar un paquete, que incluye la instalación como parte de un proceso de restauración de , NuGet también instala los paquetes adicionales en los que depende ese primer paquete.
Esas dependencias inmediatas también pueden tener dependencias propias, lo que puede continuar hasta una profundidad arbitraria. Esto genera lo que se denomina gráfico de dependencias que describe las relaciones entre paquetes en todos los niveles.
Cuando varios paquetes tienen la misma dependencia, el mismo identificador de paquete puede aparecer en el gráfico varias veces, potencialmente con restricciones de versión diferentes. Sin embargo, solo se puede usar una versión de un paquete determinado en un proyecto, por lo que NuGet debe elegir qué versión se usa. El proceso exacto depende del formato de administración de paquetes que se usa.
Resolución de dependencias con PackageReference
Al instalar paquetes en proyectos con el formato PackageReference, NuGet agrega referencias a un gráfico de paquetes planos en el archivo adecuado y resuelve los conflictos con antelación. Este proceso se conoce como restauración transitiva. La reinstalación o restauración de paquetes es un proceso de descarga de los paquetes enumerados en el gráfico, lo que da lugar a compilaciones más rápidas y predecibles.
También puede aprovechar las versiones flotantes, como 2.8.*, para evitar modificar el proyecto para usar la versión más reciente de un paquete. Al usar versiones flotantes, se recomienda habilitar la funcionalidad del archivo de bloqueo de para garantizar la repetibilidad.
Cuando el proceso de restauración de NuGet se ejecuta antes de una compilación, resuelve primero las dependencias en la memoria y, a continuación, escribe el gráfico resultante en un archivo denominado project.assets.json
.
El archivo assets se encuentra en MSBuildProjectExtensionsPath
, que tiene como valor predeterminado la carpeta "obj" del proyecto.
A continuación, MSBuild lee este archivo y lo traduce en un conjunto de carpetas donde se pueden encontrar posibles referencias y, a continuación, los agrega al árbol del proyecto en la memoria.
El archivo project.assets.json
es temporal y no debe agregarse al control de código fuente. Se muestra de forma predeterminada en .gitignore
y .tfignore
. Consulte Paquetes y control de código fuente.
Reglas de resolución de dependencias
La restauración transitiva aplica cuatro reglas principales para resolver las dependencias: versión aplicable más baja, versiones flotantes, gana dependencia directa, y dependencias de primos.
Versión aplicable más baja
La regla de versión aplicable más baja restaura la versión más baja posible de un paquete, tal como se define en sus dependencias. También se aplica a las dependencias de la aplicación o de la biblioteca de clases, a menos que se declare como flotante .
En la ilustración siguiente, por ejemplo, 1.0-beta se considera inferior a 1.0, por lo que NuGet elige la versión 1.0:
En la ilustración siguiente, la versión 2.1 no está disponible en la fuente, sino porque la restricción de versión es >= 2.1 NuGet elige la siguiente versión más baja que puede encontrar, en este caso 2.2:
Cuando una aplicación especifica un número de versión exacto, como 1.2, que no está disponible en la fuente, NuGet produce un error al intentar instalar o restaurar el paquete:
Versiones flotantes
Se especifica una versión de dependencia flotante con el carácter * . Por ejemplo, 6.0.*
. Esta especificación de versión dice "usar la versión 6.0.x más reciente"; 4.*
significa "usar la versión 4.x más reciente". El uso de una versión flotante reduce los cambios en el archivo de proyecto, al tiempo que se mantiene actualizado con la versión más reciente de una dependencia.
Las versiones flotantes solo se pueden especificar en el nivel de proyecto.
Al usar una versión flotante, NuGet resuelve la versión más alta de un paquete que coincide con el patrón de versión, por ejemplo, 6.0.*
obtiene la versión más alta de un paquete que comienza con la versión 6.0:
Versión | Versiones presentes en el servidor | Resolución | Razón | Notas |
---|---|---|---|---|
* | 1.1.0 1.1.1 1.2.0 1.3.0-alpha |
1.2.0 | La versión estable más alta. | |
1.1.* | 1.1.0 1.1.1 1.1.2-alpha 1.2.0-alpha |
1.1.1 | La versión estable más alta que respeta el patrón especificado. | |
*-* | 1.1.0 1.1.1 1.1.2-alpha 1.3.0-beta |
1.3.0-beta | La versión más alta, incluidas las versiones no estables. | Disponible en la versión 16.6 de Visual Studio, NuGet versión 5.6, SDK de .NET Core versión 3.1.300 |
1.1.*-* | 1.1.0 1.1.1 1.1.2-alpha 1.1.2-beta 1.3.0-beta |
1.1.2-beta | La versión más alta que respeta el patrón e incluye las versiones no estables. | Disponible en la versión 16.6 de Visual Studio, NuGet versión 5.6, SDK de .NET Core versión 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 | A pesar de tratarse de un rango de versiones con una parte de prerelease, se permiten versiones estables si coinciden con la parte estable. Dado que 1.2.0 > 1.2.0-rc.2 ha sido elegido. |
Nota
La resolución de versiones flotantes no considera si un paquete está listado o no. La resolución de las versiones flotantes se resolverá localmente si las condiciones pueden cumplirse con los paquetes de la carpeta de paquetes global.
Gana la dependencia directa
Cuando el gráfico de paquetes de una aplicación contiene versiones diferentes de un paquete en el mismo subgráfico, y una de esas versiones es una dependencia directa en ese subgráfico, esa versión se elegiría para ese subgráfico y el resto se omitirá. Este comportamiento permite que una aplicación invalide cualquier versión de paquete determinada en el gráfico de dependencias.
En el ejemplo siguiente, la aplicación depende directamente del paquete B con una restricción de versión de >=2.0.0. La aplicación también depende del paquete A que, a su vez, depende del paquete B, pero con una restricción >=1.0.0. Dado que la dependencia del paquete B 2.0.0 es una dependencia directa a la aplicación en el gráfico, se usa esa versión:
Advertencia
La regla de victorias de dependencia directa puede dar lugar a una degradación de la versión del paquete, lo que podría interrumpir otras dependencias en el gráfico. Cuando se degrada un paquete, NuGet agrega una advertencia de para alertar al usuario.
Esta regla también da como resultado una mayor eficacia con un gráfico de dependencias grande. Cuando una dependencia más cercana en el mismo subgráfico tiene una versión superior a otra, NuGet omite esa dependencia y NuGet también omite todas las dependencias restantes de esa rama del grafo.
En el diagrama siguiente, por ejemplo, dado que se usa Package C 2.0.0, NuGet omite las ramas de ese subgráfico que hacen referencia a una versión anterior de Package C:
A través de esta regla, NuGet intenta respetar la intención del autor del paquete. En el diagrama siguiente, el autor del paquete A ha degradado explícitamente el paquete C de la versión 2.0.0 a la versión 1.0.0.
El propietario de la aplicación puede optar por actualizar el paquete C a una versión superior a la 2.0.0, por lo que no se degrada aún más la versión del paquete C. En este caso, no se genera ninguna advertencia.
Dependencias de primos
Cuando se hace referencia a distintas versiones de paquete en subgráficos diferentes del gráfico de la aplicación, NuGet usa la versión más baja que satisface todos los requisitos de versión (como con la versión más baja y versiones flotantes reglas). En la imagen siguiente, por ejemplo, la versión 2.0.0 del paquete B satisface la otra restricción >=1.0.0 y, por tanto, se usa:
Tenga en cuenta que los paquetes no necesitan estar en la misma distancia para que se aplique la regla de dependencias de primo. En el diagrama siguiente, se elige Package D 2.0.0 en el subgráfico Package C y Package D 3.0.0 se elige en el subgráfico del Paquete A. En el subgráfico Aplicación, no hay ninguna dependencia directa en Package D, por lo que se aplica la versión más baja aplicable regla y se elige la versión 3.0.0.
En algunos casos, no es posible cumplir todos los requisitos de versión. Como se muestra a continuación, si el paquete A requiere exactamente el paquete B 1.0.0 y el paquete C requiere el paquete B >=2.0.0, NuGet no puede resolver las dependencias y da un error.
En estas situaciones, el consumidor de nivel superior (la aplicación o el paquete) debe agregar su propia dependencia directa en el paquete B para que se aplique la dependencia directa regla.
Intervalos de versiones y versiones preliminares con PackageReference
No es inusual que un paquete tenga versiones estables y preliminares disponibles.
Al resolver un gráfico de dependencias, NuGet decide si se deben tener en cuenta las versiones preliminares de un paquete en función de una sola regla: 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.
En la práctica, en la regla aplicable más baja, esto significa:
Intervalo de versiones | Versiones disponibles | Versión seleccionada |
---|---|---|
[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 | Ninguno, se genera NU1103. |
[1.0.0, 2.0.0-rc) | 1.2.0-beta.1, 2.0.0-beta.3 | 1.2.0-beta.1 |
Resolución de dependencias con packages.config
Con packages.config
, las dependencias de un proyecto se escriben en packages.config
como una lista plana. Las dependencias de esos paquetes también se escriben en la misma lista. Cuando se instalan paquetes, NuGet también puede modificar el archivo de .csproj
, app.config
, web.config
y otros archivos individuales.
Con packages.config
, NuGet intenta resolver conflictos de dependencias durante la instalación de cada paquete individual. Es decir, si el paquete A se está instalando y depende del paquete B, y el paquete B ya aparece en packages.config
como dependencia de otra cosa, NuGet compara las versiones del paquete B que se solicitan e intenta encontrar una versión que cumpla todas las restricciones de versión. En concreto, NuGet selecciona la versión más baja mayor.menor que satisface las dependencias.
De forma predeterminada, NuGet 2.8 busca la versión de revisión más baja (consulte notas de la versión de NuGet 2.8). Puede controlar esta configuración a través del atributo DependencyVersion
en NuGet.Config
y el modificador -DependencyVersion
en la línea de comandos.
El proceso de packages.config
para resolver dependencias se complica para gráficos de dependencias más grandes. Cada nueva instalación de paquetes requiere un recorrido de todo el gráfico y genera la posibilidad de conflictos de versión. Cuando se produce un conflicto, se detiene la instalación, dejando el proyecto en un estado indeterminado, especialmente con posibles modificaciones en el propio archivo del proyecto. Esto no es un problema al usar otros formatos de administración de paquetes.
Intervalos de versiones y versiones preliminares con packages.config
packages.config resolución no permite la combinación de dependencias estables y de versión preliminar en un gráfico.
Si una dependencia se expresa con un intervalo como [1.0.0, 2.0.0)
, no se permiten paquetes de versión preliminar en el gráfico.
Administración de recursos de dependencia
Al usar el formato PackageReference, puede controlar qué recursos de las dependencias fluyen al proyecto de nivel superior. Para obtener más información, consulte PackageReference.
Cuando el proyecto de nivel superior es en sí mismo un paquete, también tiene control sobre este flujo mediante el uso de los atributos include
y exclude
con dependencias enumeradas en el archivo .nuspec
. Consulte referencia de .nuspec: dependencias.
Exclusión de referencias
Hay escenarios en los que se puede hacer referencia a ensamblados con el mismo nombre más de una vez en un proyecto, lo que genera errores en tiempo de diseño y en tiempo de compilación. Considere un proyecto que contiene una versión personalizada de C.dll
y hace referencia al paquete C que también contiene C.dll
. Al mismo tiempo, el proyecto también depende del paquete B, que también depende del paquete C y C.dll
. Como resultado, NuGet no puede determinar qué C.dll
usar, pero no solo puede quitar la dependencia del proyecto en el paquete C porque el paquete B también depende de él.
Para resolverlo, debe hacer referencia directamente al C.dll
que desea (o usar otro paquete que haga referencia al correcto) y, a continuación, añadir una dependencia al paquete C que excluya todos sus recursos. Esto se hace de la siguiente manera en función del formato de administración de paquetes en uso:
PackageReference: añadir
ExcludeAssets="All"
en la dependencia:<PackageReference Include="PackageC" Version="1.0.0" ExcludeAssets="All" />
packages.config
: quite la referencia a PackageC del archivo.csproj
para que haga referencia solo a la versión deC.dll
que desee.
Actualizaciones de dependencias durante la instalación del paquete
Si ya se cumple una versión de dependencia, la dependencia no se actualiza durante otras instalaciones del paquete. Por ejemplo, considere el paquete A que depende del paquete B y especifica 1.0 para el número de versión. El repositorio de origen contiene las versiones 1.0, 1.1 y 1.2 del paquete B. Si A está instalado en un proyecto que ya contiene B versión 1.0, B 1.0 permanece en uso porque satisface la restricción de versión. Sin embargo, si el paquete A tenía solicitudes de la versión 1.1 o posterior de B, se instalaría B 1.2.
Resolución de errores de paquete incompatibles
Durante una operación de restauración de paquetes, es posible que vea el error "Uno o más paquetes no son compatibles..." o que un paquete "no es compatible" con el marco de destino del proyecto.
Este error se produce cuando uno o varios de los paquetes a los que se hace referencia en el proyecto no indican que admiten el marco de destino del proyecto; es decir, el paquete no contiene un archivo DLL adecuado en su carpeta lib
para un marco de destino compatible con el proyecto. (Consulte plataformas de destino para obtener una lista).
Por ejemplo, si un proyecto tiene como destino netstandard1.6
e intenta instalar un paquete que contiene archivos DLL solo en las carpetas lib\net20
y \lib\net45
, verá mensajes como los siguientes para el paquete y, posiblemente, sus dependientes:
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'.
Para resolver las incompatibilidades, realice una de las siguientes acciones:
- Vuelva a asignar el proyecto a un marco compatible con los paquetes que quiere usar.
- Póngase en contacto con el autor de los paquetes y trabaje con ellos para agregar compatibilidad con su marco elegido. Cada página de listado de paquetes de nuget.org tiene un enlace Contact Owners para este propósito.
Propina
solución alternativa: NuGetSolver es una extensión de Visual Studio desarrollada por Microsoft DevLabs, diseñada para ayudar a resolver conflictos de dependencias. Automatiza el proceso de identificación y resolución de estos problemas. Para obtener más información, visite la página NuGetSolver en Visual Studio Marketplace y nos encantaría escuchar sus comentarios sobre su experiencia.