Compartir a través de


Seleccionar ensamblados a los que hacen referencia proyectos

Los ensamblados se usan de dos maneras diferentes durante una construcción. La primera es para compilar, lo que permite que el código del consumidor del paquete se compile contra las API del ensamblado y para que IntelliSense proporcione sugerencias. El segundo es el tiempo de ejecución, donde el ensamblado se copia en el bin directorio y se usa durante la ejecución del programa. Algunos autores de paquetes desean solo sus propios ensamblados (o un subconjunto de sus ensamblados) disponibles para sus consumidores de paquetes en tiempo de compilación, pero deben proporcionar todas sus dependencias para tiempo de ejecución. En este documento se examinan las formas de lograr este resultado.

Nuestra recomendación es tener un paquete por ensamblado y que los paquetes tengan dependencias con otros ensamblados. Cuando NuGet restaura un proyecto, realiza la selección de recursos y admite la inclusión, exclusión y creación de clases de recursos privadas diferentes. Para evitar que las dependencias del paquete se conviertan en recursos en tiempo de compilación para cualquier usuario que use el paquete, puede hacer que compile los recursos sean privados. En el paquete generado, esto hará que compile sea excluido de la dependencia. Cuando no se proporciona ninguno, tenga en cuenta que los recursos privados predeterminados son contentfiles;build;analyzers. Por lo tanto, debe usar PrivateAssets="compile;contentfiles;build;analyzers" en PackageReference o ProjectReference.

<ItemGroup>
  <ProjectReference Include="..\OtherProject\OtherProject.csproj" PrivateAssets="compile;contentfiles;build;analyzers" />
  <PackageReference Include="SomePackage" Version="1.2.3" PrivateAssets="compile;contentfiles;build;analyzers" />
</ItemGroup>

Si va a crear un paquete a partir de un archivo personalizado nuspec , en lugar de permitir que NuGet genere automáticamente uno, nuspec debe usar el exclude atributo XML .

<dependencies>
  <group targetFramework=".NETFramework4.8">
    <dependency id="OtherProject" version="3.2.1" exclude="Compile,Build,Analyzers" />
    <dependency id="SomePackage" version="1.2.3" exclude="Compile,Build,Analyzers" />
  </group>
</dependencies>

Hay tres razones por las que se trata de la solución recomendada.

En primer lugar, los ensamblados útiles a menudo son referenciados por nuevos ensamblados o paquetes. Aunque un ensamblado de utilidad podría ser utilizado solo por un único paquete en la actualidad, lo que hace que resulte tentador enviar ambos ensamblados en un único paquete, si un segundo paquete quiere usar el ensamblado de utilidad "privado" en el futuro, el ensamblado de utilidad debe moverse a un nuevo paquete y el paquete anterior debe actualizarse para declararlo como una dependencia, o bien, el paquete de utilidad debe enviarse tanto en el paquete existente como en el nuevo. Si el ensamblado se incluye en dos paquetes diferentes y un proyecto hace referencia a ambos paquetes, si hay versiones diferentes del ensamblado de la utilidad en los dos paquetes, NuGet no podrá ayudar en la administración de versiones.

En segundo lugar, puede haber ocasiones en que los desarrolladores que usan el paquete también quieran usar las API de las dependencias. Por ejemplo, considere el paquete Microsoft.ServiceHub.Client versión 3.0.3078. Si descarga el paquete y comprueba el nuspec archivo, puede ver que enumera dos paquetes a partir de Microsoft.VisualStudio. como dependencias, lo que significa que los necesita en tiempo de ejecución, pero también excluye sus recursos de compilación. Esto significa que los proyectos que usan Microsoft.ServiceHub.Client no tendrán las API de Visual Studio disponibles en IntelliSense o si compilan el proyecto, a menos que el proyecto instale explícitamente esos paquetes. Y esta es la ventaja que una dependencia de paquete tiene con un activo excluido. Los proyectos que usan el paquete, si también quieren usar las dependencias, pueden agregar una referencia al paquete para que las API estén disponibles para sí mismas.

Por último, algunos autores de paquetes se han confundido anteriormente sobre la selección de ensamblados por parte de NuGet para paquetes que admiten más de un entorno de destino, especialmente cuando su paquete también contiene múltiples ensamblados. Si su ensamblado principal soporta diferentes marcos de destino que su ensamblado de utilidad, puede que no sea obvio en qué directorios lib/ se deben colocar todos los ensamblados. Al separar cada paquete por nombre de ensamblaje, resulta más intuitivo en qué lib/ carpetas debería ubicarse cada ensamblaje. Tenga en cuenta que esto no significa tener paquetes Package1.net48 y Package1.net6.0. Significa tener lib/net48/Package1.dll y lib/net6.0/Package6.0 en Package1y lib/netstandard2.0/Package2.dlllib/net5.0/Package2.dll en Package2. Cuando Nuget restaura un proyecto, Nuget realizará de forma independiente la selección de recursos para los dos paquetes.

También tenga en cuenta que los proyectos que usan PackageReference solo utilizan los recursos de inclusión/exclusión de activos de dependencias. Cualquier proyecto que instale el paquete mediante packages.config instalará las dependencias y también tendrá sus API disponibles. packages.config solo es compatible con las plantillas de proyecto de .NET Framework anteriores de Visual Studio. Los proyectos de estilo del SDK, incluso aquellos que tienen como destino .NET Framework, no admiten packages.configy, por tanto, admiten activos de inclusión o exclusión de dependencias.

PackageReference y packages.config tienen diferentes características disponibles. Tanto si quiere dar soporte a los consumidores de paquetes que usan PackageReference, packages.config o ambos, esto afecta el modo en que debe crear el paquete.

El destino MSBuild Pack de NuGet no admite la inclusión automática de referencias de proyecto en el paquete. Solo enumerará los proyectos a los que se hace referencia como dependencias del paquete. Hay un problema en GitHub, donde los miembros de la comunidad comparten formas de lograr este resultado, lo que suele implicar el uso PackagePath de metadatos de elementos de MSBuild para colocar archivos en cualquier parte del paquete, como se describe en los documentos sobre la inclusión de contenido en un paquete y el uso SuppressDependenciesWhenPacking de para evitar que las referencias del proyecto se conviertan en dependencias de paquetes. También existen herramientas desarrolladas por la comunidad que se pueden usar como alternativa al paquete oficial de NuGet, que admite esta característica.

Compatibilidad con PackageReference

Cuando un consumidor de paquetes usa PackageReference, NuGet selecciona los recursos de compilación y tiempo de ejecución de forma independiente, como se ha descrito anteriormente.

Los recursos de compilación prefieren ref/<tfm>/*.dll (por ejemplo ref/net6.0/*.dll), pero si eso no existe, se revertirá a lib/<tfm>/*.dll (por ejemplo lib/net6.0/*.dll, ).

Los recursos en tiempo de ejecución prefieren runtimes/<rid>/lib/<tfm>/*.dll (por ejemplo (runtimes/win11-x64/lib/net6.0/*.dll)), pero si eso no existe, se revertirá a lib/<tfm>/*.dll.

Dado que los ensamblados en ref\<tfm>\ no se utilizan en tiempo de ejecución, estos pueden ser ensamblados solo de metadatos con el fin de reducir el tamaño del paquete.

Compatibilidad con packages.config

Los proyectos que usan packages.config para administrar paquetes NuGet normalmente agregan referencias a todos los ensamblados del lib\<tfm>\ directorio. El ref\ directorio se agregó para admitir PackageReference y, por lo tanto, no se considera al usar packages.config. Para establecer explícitamente los ensamblados a los que se hace referencia para los proyectos que usan packages.config, el paquete debe usar el <references> elemento en el archivo nuspec. Por ejemplo:

<references>
    <group targetFramework="net45">
        <reference file="MyLibrary.dll" />
    </group>
</references>

Los objetivos del paquete MSBuild no admiten el elemento <references>. Consulte los documentos sobre el empaquetado mediante un archivo .nuspec al usar MSBuild Pack.

Nota:

packages.config project utiliza un proceso denominado ResolveAssemblyReference para copiar ensamblados al bin\<configuration>\ directorio de salida. El ensamblado de tu proyecto se copia, luego el sistema de compilación examina el manifiesto del ensamblaje para los ensamblajes referenciados, copiando esos ensamblajes y repitiendo el proceso de forma recursiva para todos los ensamblajes. Esto significa que si alguno de los ensamblados cargados solo por reflexión (Assembly.Load, MEF u otro framework de inyección de dependencias), es posible que no se copie al directorio de salida del bin\<configuration>\ proyecto a pesar de estar en bin\<tfm>\. Esto también significa que esto solo funciona para los ensamblados de .NET, no para el código nativo al que se llama con P/Invoke.

Compatibilidad con ambos PackageReference y packages.config

Importante

Si un paquete contiene el elemento nuspec <references> y no contiene ensamblados en ref\<tfm>\, NuGet anunciará los ensamblados enumerados en el elemento nuspec <references> como los recursos de compilación y tiempo de ejecución. Esto significa que habrá excepciones en tiempo de ejecución cuando los ensamblados a los que se hace referencia necesiten cargar cualquier otro ensamblado en el lib\<tfm>\ directorio. Por lo tanto, es importante usar tanto el nuspec <references> para el soporte de packages.config, como duplicar ensamblados en la carpeta ref/ para el soporte de PackageReference. No es necesario usar la carpeta del paquete runtimes/, se agregó en la sección anterior para completar la información.

Example

Mi paquete contendrá tres ensamblados, MyLib.dll, MyHelpers.dll y MyUtilities.dll, que tienen como destino .NET Framework 4.7.2. MyUtilities.dll contiene clases diseñadas para que solo las usen los otros dos ensamblados, por lo que no quiero que esas clases estén disponibles en IntelliSense ni en tiempo de compilación para los proyectos que usan mi paquete. Mi nuspec archivo debe contener los siguientes elementos XML:

<references>
    <group targetFramework="net472">
        <reference file="MyLib.dll" />
        <reference file="MyHelpers.dll" />
    </group>
</references>

Necesito asegurarme de que el contenido del paquete sea:

lib\net472\MyLib.dll
lib\net472\MyHelpers.dll
lib\net472\MyUtilities.dll
ref\net472\MyLib.dll
ref\net472\MyHelpers.dll