0

.Net 8 Preview 3

.NET 8 presenta su Vista Preliminar 3, desplegando transformaciones sustanciales en las rutas de compilación, las tareas, Microsoft.Extensions y la orquestación de contenedores. Este lanzamiento se distingue, además, por las optimizaciones de rendimiento introducidas en el JIT, focalizadas especialmente en Arm64 y PGO dinámico.

Echemos un vistazo a algunas características nuevas.

SDK

El SDK de .NET ha evolucionado con múltiples mejoras, marcando un hito importante con un cambio significativo. Para explorar en detalle esta modificación fundamental, se invita a revisar la sección denominada «El SDK de .NET ya no ajusta la codificación al concluir

Este avance trae consigo las siguientes mejoras implementadas en el SDK de .NET

Simplified output path

La compilación de aplicaciones en .NET se puede abordar de diversas maneras, generando una amplia y compleja red de rutas de salida para distintos artefactos de compilación. Carpetas emblemáticas como bin, obj y publish, con sus diversas permutaciones y disposiciones, se han arraigado en la memoria de muchos desarrolladores de .NET. Otro principio sólido es la organización de directorios por proyecto en estos productos. Sin embargo, a lo largo del tiempo, hemos recibido comentarios tanto de usuarios nuevos como veteranos de .NET que plantean la siguiente percepción sobre este diseño:

La complejidad de la utilización se manifiesta en la capacidad de sufrir cambios significativos en la estructura mediante ajustes aparentemente simples en MSBuild. Además, la dificultad de anticipación por parte de las herramientas se acentúa, dado que la organización por proyecto dificulta la certeza de haber capturado completamente los resultados de cada uno.

Para afrontar eficazmente ambos desafíos y optimizar la facilidad de uso y coherencia de las salidas de compilación, el SDK de .NET ha introducido una opción que establece una estructura de ruta de salida más homogénea y simplificada.

La nueva ruta de salida se centra en:

  • Reunir todos los resultados de la compilación en una ubicación común.
  • Separar los resultados de compilación por proyecto en esta ubicación común.
  • Aplanar los diseños generales de salida de la compilación a un máximo de tres niveles de profundidad.

Para activar la nueva estructura de ruta de salida, es esencial establecer la propiedad UseArtifactsOutput en un archivo Directory.Build.props. Una forma práctica de iniciar este proceso es ejecutar dotnet new buildprops en la carpeta principal de tu repositorio, abrir el archivo Directory.Build.props generado y añadir la siguiente configuración al grupo de propiedades (PropertyGroup) en ese archivo:

<UseArtifactsOutput>true</UseArtifactsOutput>

A partir de este punto, la salida de compilación para todos los proyectos se colocará en el directorio .artifacts en la raíz del repositorio. Esto es configurable; simplemente establece la propiedad ArtifactsPath en tu archivo Directory.Build.props a cualquier directorio que prefieras. Si no deseas utilizar .artifacts como predeterminado, nos encantaría recibir comentarios al respecto en la discusión sobre el diseño.

El diseño del directorio .artifacts seguirá la forma <RutaDeArtefactos><TipoDeSalida><NombreDelProyecto><Pivotes>, donde:

La categorización del «Tipo de Resultado» se utiliza para clasificar diferentes tipos de salidas de compilación, como binarios, archivos intermedios o generados, aplicaciones publicadas o paquetes NuGet. Por otra parte, «Pivotes» se emplea para estandarizar todas las opciones distintas que se utilizan para diferenciar las compilaciones, tales como Configuración (Configuration) e Identificador de Tiempo de Ejecución (RuntimeIdentifier).»

Algunos ejemplos de rutas que se crearían bajo el nuevo formato son:

  • Binaries: <ArtifactsPath>\Binaries\ProjectName\Pivots
  • Intermediate/GeneratedFiles: <ArtifactsPath>\Intermediate\ProjectName\Pivots
  • Published Applications: <ArtifactsPath>\Published\ProjectName\Pivots
  • NuGet Packages: <ArtifactsPath>\NuGetPackages\ProjectName\Pivots

Donde <ArtifactsPath> representa la ruta base configurada para los artefactos, «ProjectName» es el nombre del proyecto y «Pivots» son las opciones que diferencian las compilaciones, como Configuración (Configuration) e Identificador de Tiempo de Ejecución (RuntimeIdentifier).

dotnet workload clean command

A lo largo de diversas actualizaciones del SDK de .NET y Visual Studio, es plausible que los paquetes de carga de trabajo (las unidades específicas de funcionalidad, herramientas y plantillas que constituyen una carga de trabajo) queden en segundo plano. Esta situación puede deberse a varias razones, pero en todos los casos resulta desconcertante para los usuarios finales. Algunos usuarios eligen eliminar manualmente los directorios de carga de trabajo de las ubicaciones de instalación del SDK, aunque el equipo del SDK no respalda esta medida drástica. En lugar de seguir ese enfoque, en esta versión preliminar se introduce un nuevo comando para simplificar la limpieza de los paquetes de carga de trabajo no utilizados.

dotnet workload clean

dotnet workload clean

Ejecuta la recolección de residuos de cargas de trabajo para aquellas basadas en archivos o MSI. De este modo, la recolección de basura se comporta de manera habitual, eliminando exclusivamente los paquetes huérfanos en sí mismos.

Eliminará los paquetes huérfanos de versiones no instaladas del SDK de .NET o paquetes donde los registros de instalación ya no existan. Este proceso se llevará a cabo únicamente para la versión de SDK específica o versiones anteriores. Si cuentas con una versión más reciente del SDK instalada, deberás repetir el comando.

Si Visual Studio está instalado y también ha gestionado cargas de trabajo, dotnet workload clean enumerará todas las cargas de trabajo de Visual Studio instaladas en la máquina y advertirá que deben desinstalarse a través de Visual Studio en lugar de la CLI de .NET SDK. Esto se realiza para brindar claridad sobre por qué algunas cargas de trabajo no se limpian o desinstalan después de ejecutar dotnet workload clean.

dotnet workload clean –all

A diferencia de la limpieza de cargas de trabajo, dotnet workload clean –all

ejecuta la recolección de basura de manera excepcional, lo que significa que elimina todos los paquetes existentes en la máquina que no son de Visual Studio y pertenecen al tipo de instalación de carga de trabajo del SDK actual (ya sea basado en archivos o basado en MSI).

Debido a esto, también borra todos los registros de instalación de cargas de trabajo para la banda de características actual del SDK de .NET y versiones inferiores. La limpieza de cargas de trabajo aún no elimina los registros de instalación, ya que los manifiestos son actualmente la única manera de asignar un paquete al ID de la carga de trabajo, pero los archivos de manifiesto pueden no existir para paquetes huérfanos.

Runtime

ValidateOptionsResultBuilder

El Constructor de Resultado de Validación de Opciones simplifica la creación de un objeto Resultado de Validación de Opciones, esencial para la implementación de IValidateOptions.Validate(String, TOptions). Este constructor te posibilita reunir diversos errores, permitiéndote visualizar todos los problemas de una vez y abordarlos según sea necesario. Esta innovadora herramienta te permite ahorrar tiempo y esfuerzo al simplificar el procedimiento de validación:

ValidateOptionsResultBuilder builder = new();
builder.AddError("Error: invalid operation code");
builder.AddResult(ValidateOptionsResult.Fail("Invalid request parameters"));
builder.AddError("Malformed link", "Url");

// Build ValidateOptionsResult object has accumulating multiple errors.
ValidateOptionsResult result = builder.Build();

// Reset the builder to allow using it in new validation operation.
builder.Clear();

Generador de origen de enlace de configuración.

La configuración de la aplicación en ASP.NET Core se lleva a cabo mediante uno o más proveedores de configuración. Estos proveedores recuperan datos, como pares clave-valor, de diversas fuentes, como archivos de configuración (por ejemplo, appsettings.json), variables de entorno y Azure Key Vault, entre otros.

En la esencia de este mecanismo se halla ConfigurationBinder, una clase de extensión que ofrece los métodos Bind y Get. Estos métodos asignan valores de configuración (instancias de IConfiguration) a objetos fuertemente tipados. Bind recibe una instancia, mientras que Get crea una en nombre del llamante. El método actual emplea reflexión, lo cual genera inconvenientes para la reducción y la AOT nativa.

En .NET 8, se implementó un generador de código fuente que produce implementaciones de enlace sin reflexión y compatibles con AOT. El generador busca llamadas a Configure, Bind y Get de las cuales podemos obtener información de tipo.

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
IConfigurationSection section = builder.Configuration.GetSection("MyOptions");

// !! Configure call - to be replaced with source-gen'd implementation
builder.Services.Configure<MyOptions>(section);

// !! Get call - to be replaced with source-gen'd implementation
MyOptions options0 = section.Get<MyOptions>();

// !! Bind call - to be replaced with source-gen'd implementation
MyOptions options1 = new MyOptions();
section.Bind(myOptions1);

WebApplication app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();

public class MyOptions
{
    public int A { get; set; }
    public string S { get; set; }
    public byte[] Data { get; set; }
    public Dictionary<string, string> Values { get; set; }
    public List<MyClass> Values2 { get; set; }
}

public class MyClass
{
    public int SomethingElse { get; set; }
}

Cuando el generador se encuentra activo en un proyecto, los métodos generados son automáticamente recopilados por el compilador, en lugar de depender de las implementaciones basadas en reflexión del marco ya existente. Para habilitar el generador de código fuente, descarga la última versión preliminar de Microsoft.Extensions.Configuration.Binder. El generador viene desactivado por defecto. Para su utilización, incorpora la siguiente propiedad a tu archivo de proyecto:

<PropertyGroup>
    <EnableMicrosoftExtensionsConfigurationBinderSourceGenerator>true</EnableMicrosoftExtensionsConfigurationBinderSourceGenerator>
</PropertyGroup>

Containers

Se continua mejorando las capacidades y la experiencia de usar .NET en contenedores. En esta versión se centra en la seguridad y en apuntar a varias arquitecturas.

Construir imágenes de contenedores multiplataforma

Ahora es común utilizar máquinas tanto Arm64 como x64 de manera regular. Las máquinas x64 han estado presentes durante décadas; sin embargo, las máquinas de desarrollo Arm64 (como las Mac de Apple) y los nodos en la nube Arm64 son relativamente nuevos. Docker admite el uso y la construcción de imágenes multiplataforma que funcionan en múltiples entornos. Hemos desarrollado un nuevo patrón que te permite combinar arquitecturas con las imágenes de .NET que construyes.

Imagina que estás en una Mac de Apple y deseas apuntar a un servicio en la nube x64 en Azure. Puedes construir la imagen utilizando el interruptor –platform de la siguiente manera.

docker build --pull -t app --platform linux/amd64 .

Con el nuevo patrón, actualizará solo una línea en el Dockerfile (la fase de compilación):

FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-preview-alpine AS build

Esa línea habilita que el SDK se ejecute en la arquitectura de la máquina local, lo que hará que funcione de manera más rápida y compatible (ya que .NET no es compatible con QEMU). Esta línea no requirió un cambio en .NET, pero es una característica útil de Docker.

También actualizamos el SDK (en la Vista previa 3) para admitir los valores de $TARGETARCH y agregar el argumento -a en la restauración. Puedes ver eso en el siguiente ejemplo.

RUN dotnet restore -a $TARGETARCH

# copy everything else and build app
COPY aspnetapp/. .
RUN dotnet publish -a $TARGETARCH --self-contained false --no-restore -o /app

Variable de entorno para el valor de UID de usuario no raíz

Se ha agregado una variable de entorno para el UID para el usuario no root que agregamos en la versión preliminar 1. Nos dimos cuenta de que la prueba runAsNonRoot de Kubernetes requería que el usuario del contenedor se estableciera a través de UID, no de name. Al mismo tiempo, queríamos evitar que los desarrolladores tuvieran que aplicar un número especial a través de miles de Dockerfiles (colectivamente). En su lugar, estamos exponiendo ese valor en un entorno variable.

USER $APP_UID

Conclusiones

El lanzamiento de la versión preliminar de .NET 8 presenta avances significativos centrados en la construcción de imágenes de contenedores multiplataforma. Introduciendo un generador de código fuente, ahora es posible generar implementaciones de enlace sin reflexión y compatibles con AOT, mejorando la seguridad y facilitando el desarrollo en arquitecturas diversas. Esta versión aborda la creciente prevalencia de máquinas Arm64 y x64, permitiendo a los desarrolladores construir imágenes que funcionen en entornos heterogéneos. Además, se destaca el soporte de valores $TARGETARCH y el argumento -a en la restauración del SDK. Con estas mejoras, .NET 8 preview ofrece una experiencia más eficiente y flexible para la construcción y distribución de aplicaciones en contenedores.

Fernando Sonego

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *