DEV Community

Cover image for Domina el uso de paquetes NuGet en .NET
Manuel Dávila Alarcón
Manuel Dávila Alarcón

Posted on

Domina el uso de paquetes NuGet en .NET

¿Alguna vez has entrado a un proyecto Legacy (o incluso uno nuevo mal configurado), intentas compilar y tu consola se tiñe de rojo con errores tipo The type or namespace name 'X' could not be found? 😵 O peor aún, ¿te ha tocado ese escenario donde el proyecto busca una DLL en una carpeta de red Z:\Librerias que solo existía en la máquina del desarrollador que renunció hace dos años?

GIF: This is fine

Si te ha pasado, sabes la frustración de perder horas (o días) solo intentando levantar el entorno. A menudo, el problema no es el código C#, es la gestión de dependencias. Hoy vamos a arreglar esto de raíz, entendiendo cómo consumir librerías de forma profesional, segura y escalable.

¿Qué es realmente NuGet?

Imagina que NuGet es el Amazon o Mercado Libre de .NET. Tú no fabricas cada tornillo de tu mueble; los pides a la tienda.

  • El Package: Es el producto que compras (Newtonsoft.Json, EntityFramework).
  • El Source (Feed): Es el almacén o la tienda donde se guardan los paquetes.

¿A dónde van mis paquetes?

Muchos desarrolladores creen que cuando ejecutan dotnet restore, las DLLs "mágicamente" aparecen en su proyecto. Entender el flujo real es vital para saber qué está pasando por detrás:

  1. Request: Tu proyecto (.csproj) pide Newtonsoft.Json v13.0.1.
  2. Restore: NuGet busca en los Sources configurados.
  3. Global Packages Folder: Aquí está el secreto. NuGet descarga y descomprime el paquete en una carpeta global de tu usuario (generalmente %userprofile%\.nuget\packages en Windows o ~/.nuget/packages en Linux/Mac). No se guardan dentro de tu proyecto.
  4. Build: Cuando compilas, .NET lee las DLLs de esa carpeta global y las copia a tu carpeta bin/Debug o bin/Release.

Diagrama de flujo: Ciclo de vida de un paquete NuGet

¿Por qué importa esto? Porque optimiza el espacio. Si tienes 10 proyectos usando la misma librería, solo se descarga una vez en tu disco duro.

La jerarquía de configuración

Antes de escribir una sola línea de configuración, debes entender cómo "piensa" NuGet. Cuando ejecutas un restore, no solo mira tu proyecto. NuGet combina configuraciones en cascada siguiendo una jerarquía estricta:

  • Nivel Máquina: A menudo este archivo ni siquiera existe físicamente o está vacío, a menos que un administrador de sistemas lo haya puesto ahí.
  • Nivel Usuario: Este es el más común (%appdata%\NuGet\NuGet.Config) y suele acumular mucha "basura digital" con el tiempo.
  • Nivel Directorio (Recursivo): ¡Aquí está la trampa! NuGet busca en la carpeta de tu solución... pero si no encuentra lo que busca, sube a la carpeta padre, y luego a la del abuelo, hasta llegar a la raíz del disco.

El origen del "En mi máquina funciona" 🤷‍♂️

Meme: It works on my machine

Aquí nace el famoso meme. Imagina que tú tienes configurado un feed privado en tu Nivel Usuario porque trabajas en varios proyectos de la empresa. Creas un proyecto nuevo y compila perfecto. Tu compañero clona el repositorio, intenta compilar y... error.

¿Por qué? Porque tu proyecto está dependiendo de una configuración "invisible" que vive solo en tu usuario. Para tu compañero, ese feed no existe.

¿Cómo detecto esto? No adivines. Sitúate en la carpeta de tu solución y ejecuta el siguiente comando:

dotnet nuget list source
Enter fullscreen mode Exit fullscreen mode

Este comando te mostrará la lista final y combinada de todos los orígenes que NuGet está usando realmente en esa carpeta. Si ves rutas que no reconoces o servidores apagados, es culpa de la herencia.

NuGet.config

¿Cómo rompemos esa cadena de herencia tóxica y arreglamos el proyecto para todos? Creando un archivo NuGet.config explícito en la raíz de tu solución.

Aquí tienes una configuración que cubre los escenarios reales:

<configuration>
  <packageSources>
    <!-- ¡IMPORTANTE! <clear/> elimina toda la "basura" heredada del usuario o máquina -->
    <clear />

    <!-- 1. El estándar público -->
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />

    <!-- 2. Nube Privada (GitHub Packages, Azure, AWS) -->
    <!-- Ideal para equipos modernos y CI/CD -->
    <add key="GitHubFeed" value="https://nuget.pkg.github.com/mi-empresa/index.json" />

    <!-- 3. Servidor NuGet Local (BaGet o similar) -->
    <add key="BadGetFeed" value="http://servidor-interno:5555/v3/index.json" />

    <!-- 4. Carpeta Local en Servidor On-Premise -->
    <add key="OnPremiseFeed" value="./LibreriasAntiguas" />
  </packageSources>

  <!-- Puedes deshabilitar fuentes temporalmente sin borrar la línea -->
  <disabledPackageSources>
     <add key="OnPremiseFeed" value="true" />
  </disabledPackageSources>

  <packageSourceCredentials>
    <!-- Autenticación Segura para feeds privados (Usa variables de entorno) -->
    <GitHubFeed>
        <add key="Username" value="DevUser" />
        <add key="ClearTextPassword" value="%GITHUB_TOKEN%" />
    </GitHubFeed>
  </packageSourceCredentials>
</configuration>
Enter fullscreen mode Exit fullscreen mode

Beneficios de configurar esto:

  • 🛡 Aislamiento: Con <clear />, proteges tu proyecto de configuraciones globales rotas.
  • 🛠 Independencia: Si un dev nuevo entra, solo hace git clone y dotnet restore. No necesita configurar nada manual.
  • 🔄 CI/CD Friendly: Tus Pipelines sabrán exactamente dónde buscar sin pasos extraños.

El caos de la prioridad

Aquí es donde muchos desarrolladores fallan. Si tienes configurado nuget.org y GitHubFeed, y ambos tienen un package llamado Newtonsoft.Json... ¿cuál se descarga?

Meme Spiderman señalándose: Conflicto de nombres de paquetes

NuGet actua "al primero que responda". Esto es peligroso por dos razones:

  • Rendimiento: NuGet pierde tiempo buscando tu package privado en la tienda pública.
  • Seguridad: Si un atacante sube un paquete malicioso a nuget.org con el mismo nombre que tu paquete privado interno (ej: MiBanco.Core), ¡tu proyecto podría descargar el package del atacante sin darte cuenta! 🚨

Diagrama de amenaza: Dependency Confusion

Para solucionar el caos de prioridad y asegurar qué bajamos, tenemos dos herramientas clave:

1. Package Source Mapping

Es básicamente decirle a NuGet: "Los paquetes de Microsoft búscalos en la tienda pública, y los paquetes de mi empresa SOLO búscalos en mis servidores".

<configuration>
  <!-- ... sección packageSources definida arriba ... -->

  <!-- AQUÍ ESTÁ LA MAGIA -->
  <packageSourceMapping>

    <!-- Regla 1: Mis librerías Core van a GitHub Packages -->
    <packageSource key="GitHubFeed">
      <package pattern="MyCompany.Core.*" />
      <package pattern="MyCompany.Auth.*" />
    </packageSource>

    <!-- Regla 2: Librerías internas van al Server NuGet Privado -->
    <packageSource key="BadGetFeed">
      <package pattern="InternalTools.*" />
    </packageSource>

    <!-- Regla 3: Componentes viejos van a la carpeta local -->
    <packageSource key="OnPremiseFeed">
       <package pattern="OldComponent.WinForms.*" />
    </packageSource>

    <!-- Regla 4: Todo lo demás (Microsoft, System, etc.), búscalo en nuget.org -->
    <packageSource key="nuget.org">
      <package pattern="*" />
    </packageSource>

  </packageSourceMapping>
</configuration>
Enter fullscreen mode Exit fullscreen mode

2. Auditoría de vulnerabilidades

A veces descargas el paquete correcto, pero tiene agujeros de seguridad conocidos. No necesitas herramientas caras; .NET lo trae nativo.
Ejecuta esto regularmente o en tu CI/CD:

dotnet list package --vulnerable
Enter fullscreen mode Exit fullscreen mode

Te dirá qué paquetes tienen riesgos (Crítico, alto o moderado) y a qué versión segura actualizar.

Central Package Management (CPM)

Si trabajas en una solución con muchos servicios, seguro te ha pasado esto:

  • El Proyecto A usa Newtonsoft.Json v11.
  • El Proyecto B usa Newtonsoft.Json v13.
  • El Proyecto C usa Newtonsoft.Json v9.

¡Es un caos de versiones! 🤯

Para solucionar esto en proyectos modernos, usamos Central Package Management (CPM). En lugar de definir la versión en cada .csproj, las centralizamos.

Comparativa: Caos de versiones vs Central Package Management

Paso 1: Crear archivo Directory.Packages.props en la raíz

<Project>
  <PropertyGroup>
    <!-- Activar CPM -->
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <!-- Aquí defines la versión UNA SOLA VEZ para toda la solución -->
    <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
    <PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
  </ItemGroup>
</Project>
Enter fullscreen mode Exit fullscreen mode

Paso 2: Limpiar tus .csproj

En tus proyectos individuales, ya no pones la versión, solo el nombre:

<!-- En .csproj -->
<ItemGroup>
  <!-- Sin versión porque ya lo toma de Directory.Packages.props -->
  <PackageReference Include="Newtonsoft.Json" />
</ItemGroup>
Enter fullscreen mode Exit fullscreen mode

¿Cómo aplico los cambios?

Una vez que has creado o ajustado tu archivo NuGet.config con los sources y el mapping, ve a tu terminal y ejecuta:

# Limpia cachés locales para asegurar que las reglas nuevas apliquen
dotnet nuget locals all --clear

# Restaura usando la nueva configuración
dotnet restore
Enter fullscreen mode Exit fullscreen mode

¿Por qué deberías dominar esto?

  • Resurrección de Proyectos: Puedes levantar proyectos Legacy en minutos mapeando los source que corresponde.
  • Seguridad Empresarial: Proteges a tu organización de ataques de cadena de suministro y vulnerabilidades conocidas.
  • Arquitectura Limpia: Con CPM, mantienes tus dependencias ordenadas y actualizadas sin esfuerzo.

No dejes que las dependencias te controlen a ti. ¡Toma el control de tus paquetes!

¡Happy coding! 🚀

Top comments (0)