[Article] Novedades! .Net 5 Preview 6

Cada día estamos más cerca de tener en nuestras manos la versión final de .Net 5. Como todos sabemos, DotNet 5 será la unificación de .net framework y .Net Core. Microsoft promete tenerlo listo en noviembre de este año. Hace unos días el equipo de Microsoft y Entity framework han anunciado la disponibilidad de la versión Preview 6 para ambos marcos ayudando a calmar nuestras ansiedades. ¡Veamos qué tienen de nuevo!

.NET 5.0 Preview 6

Entre la lista de mejoras y nuevas funcionalidades tenemos:

  • Windows ARM64 Update
  • Windows Forms
  • RyuJIT code quality improvements
  • Single file apps
  • Native hosted application
  • [Breaking change] Removal of built-in WinRT support in .NET 5.0

Windows ARM64 Update

En la Preview 4 se anunció la compatibilidad con Windows ARM64, pero solo se encontraba disponible en aplicaciones de consola y ASP.Net Core. Ahora, disponible el soporte para Windows Forms dándonos la oportunidad de ejecutarlas en Windows ARM64. En futuro, también podremos usar Windows Presentation foundation.

Podemos ver ejemplos completos desde este link en GitHub https://github.com/richlander/testapps/tree/master/versioninfo-windowsforms/versioninfo

El equipo de .Net espera tener lista la depuración remota en la versión 16.7 de Visual Studio y luego en Visual Studio Code.

Windows Forms

Tenemos disponible el comportamiento de ejecución de una sola instancia a través de WindowsFormsApplicationBase.IsSingleInstance. Algo muy utilizado en Visual Basic.

Otra novedad, tenemos soporte para contraer a ListViewGroup facilitando la administración de formulario con múltiples tipos de este objeto.

RyuJIT code quality improvements

RyuJIT continúa haciendo mejoras realmente importantes, con cada nueva vista previa. Aquí todos los links de demos y ejemplos:

Single file apps

El objetivo es permitir la publicación de una aplicación como un solo archivo, para todos los sistemas operativos. Se continúan agregando mejoras para dar un excelente soporte a este tipo de aplicaciones. Nueva opción para incluir binarios y cualquier otro tipo de contenido en este archivo único

El Linux, por el no está al 100% esta funcionalidad, pero seguramente estarán disponibles en la próxima preview..

Native hosted application

Se implementó un nuevo modelo para el alojamiento en las aplicaciones nativas de .Net.  Esta aprovecha todas las funcionalidades de aplicación integradas en la capa de alojamiento de aplicaciones .NET (específicamente las dependencias de carga), al tiempo que nos permite llamar a un punto de entrada personalizado desde el código nativo.

[Breaking change] Removal of built-in WinRT support in .NET 5.0

El equipo de .Net y el equipo de Windows se encuentran trabajando estrechamente para cambiar y mejorar la forma en que WinRT interopera con .Net. Este cambio es muy fuerte, por esto lo han denominado como un Breaking Change.

Algunos de los beneficios:

  • La interoperabilidad de WinRT se puede desarrollar y mejorar por separado del tiempo de ejecución de .NET.
  • La interoperabilidad de WinRT es simétrica con los sistemas de interoperabilidad proporcionados para otros sistemas operativos, como iOS y Android.
  • Podremos aprovechar muchas otras características de .NET (AOT, características de C #, vinculación IL).
  • Simplificará la base de código de tiempo de ejecución .NET.

ASP.NET Core updates in .NET 5 Preview 6

Veamos que tenemos para divertirnos en esta nueva versión:

  • Blazor WebAssembly plantilla incluida en la preview.
  • JSON extension methods para HttpRequest and HttpResponse.
  • Método de extensión para permitir el acceso anónimo a extremos.
  • Manejo personalizado para fallas de autorización.
  • Filtros en SignalR Hub.

Blazor WebAssembly  plantilla incluida en la preview.

Tenemos disponible la plantilla completa para Blazor WebAssembly dentro del framework. Esta era muy esperada por los desarrolladores de sitios SPA. Por supuesto, seguimos teniendo la plantilla para Blazor Serverside. Para generar la plantilla usaremos el siguiente comando:

dotnet new blazorwasm.

JSON extension methods para HttpRequest and HttpResponse.

Para facilitar la lectura y escritura de datos del tipo JSON desde solicitudes o respuestas http se agregaron dos nuevos métodos ReadFromJsonAsync y WriteAsJsonAsync. Ambos utilizan la clase System.Text.Json para manejar este tipo de datos. Otra utilidad nueva es HasJsonContentTyp, será útil para verificar si la solicitud tiene el tipo de contenido correspondiente. Podemos combinar estos métodos para crear API JSON de un manera ligera y rápido y utilizar el estilo que los desarrolladores llaman “route to code”.

endpoints.MapGet("/weather/{city:alpha}", async context =>{    
     var city = (string)context.Request.RouteValues["city"]; 
     var weather = GetFromDatabase(city);
     await context.Response.WriteAsJsonAsync(weather);});

Si quieres más información sobre este tema puedes verlo en este video recent On .NET interview about route to code.

Método de extensión para permitir el acceso anónimo a extremos.

Ahora podemos permitir acceso anónimo a un extremo utilizando el método AllowAnonymous:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env){    
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseEndpoints(endpoints => {
        endpoints.MapGet("/", async context => {
            await context.Response.WriteAsync("Hello World!");        
            }).AllowAnonymous();
   });
}

Manejo personalizado para fallas de autorización.

Ahora es posible registrar, por medio de Inyección de dependencias, respuestas HTTP personalizadas que fueron generadas por un fallo de autorización. Solo es necesario usar la la nueva interfaz IAuthorizationMiddlewareResultHandler quera será invocada por AuthorizationMiddleware.

Filtros en SignalR Hub

Esta es una nueva funcionalidad es como la que se encuentra disponibles ASP.Net MVC. Básicamente nos permite ejecutar código antes y después de que se invoque alguno de los métodos, de forma similar a middleware, antes y después de una solicitud HTTP.

Puedes encontrar más información aquí

Entity Framework Core EFCore 5.0 Preview 6

Esta nueva versión tiene una gran cantidad de novedades, entre ellas: división de consultas para colecciones relacionadas, nuevo atributo índice, mejora en excepciones, mapeo de direcciones ip, exposición del id de transacción, ¡y más!

Para poder utilizar esta versión tenemos que ejecutar el siguiente comando desde consola:

dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 5.0.0-preview.6.20312.4

Dividir consultas

Cuando tenemos muchas inclusiones o proyecciones, al recuperar las colecciones de objetos, puede suceder que consumamos muchos recursos ocasionando algunas demoras. Esto sucede porque Entity Framework, hasta hoy, generaba todo en una sola consulta.

Esta versión nos permite que cuando debemos consultar para cargar los includes o proyecciones dividir estas consultas para aumentar en gran medida el rendimiento.

Antes

var artists = context.Artists
    .Include(e => e.Albums).ThenInclude(e => e.Tags)
    .ToList();
SELECT "a"."Id", "a"."Name", "t0"."Id", "t0"."ArtistId", "t0"."Title", "t0"."Id0", "t0"."AlbumId", "t0"."Name"
FROM "Artists" AS "a"
LEFT JOIN (    SELECT "a0"."Id", "a0"."ArtistId", "a0"."Title", "t"."Id" AS "Id0", "t"."AlbumId", "t"."Name"    FROM "Album" AS "a0"
LEFT JOIN "Tag" AS "t" ON "a0"."Id" = "t"."AlbumId") AS "t0" ON "a"."Id" = "t0"."ArtistId"ORDER BY "a"."Id", "t0"."Id", "t0"."Id0"

Ahora

var artists = context.Artists
    .AsSplitQuery()
    .Include(e => e.Albums).ThenInclude(e => e.Tags)
    .ToList();
SELECT "a"."Id", "a"."Name"
FROM "Artists" AS "a"
ORDER BY "a"."Id"

SELECT "a0"."Id", "a0"."ArtistId", "a0"."Title", "a"."Id"
FROM "Artists" AS "a"
INNER JOIN "Album" AS "a0" ON "a"."Id" = "a0"."ArtistId"
ORDER BY "a"."Id", "a0"."Id"

SELECT "t"."Id", "t"."AlbumId", "t"."Name", "a"."Id", "a0"."Id"
FROM "Artists" AS "a"
INNER JOIN "Album" AS "a0" ON "a"."Id" = "a0"."ArtistId"
INNER JOIN "Tag" AS "t" ON "a0"."Id" = "t"."AlbumId"
ORDER BY "a"."Id", "a0"."Id"

Todas las operaciones básicas son compatibles con esta versión, como, por ejemplo, OrderBy, Skip, Join, FirstOrDefault y las operaciones de selección similares. OrderBy, Skip y Take estarán disponibles en la Preview 7.

Para proyecciones podemos utilizarlo de la siguiente manera:

context.Artists
    .AsSplitQuery()
    .Select(e => new{
       Artist = e,
       Albums = e.Albums,
    }).ToList();

El resultado de la consulta que se ejecuta en SQL será el siguiente:

SELECT "a"."Id", "a"."Name"
FROM "Artists" AS "a"
ORDER BY "a"."Id"

SELECT "a0"."Id", "a0"."ArtistId", "a0"."Title", "a"."Id"FROM "
Artists" AS "a"
INNER JOIN "Album" AS "a0" ON "a"."Id" = "a0"."ArtistId"
ORDER BY "a"."Id"

Nuevo Atributo Index

El nuevo IndexAttribute podemos colocar en un tipo de entidad especificando un índice para una sola columna. Por ejemplo:

[Index(nameof(FullName), IsUnique = true)]
public class User{
    public int Id { get; set; }
    [MaxLength(128)]
    public string FullName { get; set; }
}

Para SQL Server, con Migrations generará el siguiente SQL:

CREATE UNIQUE INDEX [IX_Users_FullName]
    ON [Users] ([FullName])
    WHERE [FullName] IS NOT NULL;

IndexAttribute podemos utilizarlo para especificar un índice que abarca varias columnas. Por ejemplo:

[Index(nameof(FirstName), nameof(LastName), IsUnique = true)]
public class User{    
    public int Id { get; set; }
    [MaxLength(64)]   
    public string FirstName { get; set; }
    [MaxLength(64)]    
    public string LastName { get; set; }
}

El resultado del script de SQL:

CREATE UNIQUE INDEX [IX_Users_FirstName_LastName]
    ON [Users] ([FirstName], [LastName])
    WHERE [FirstName] IS NOT NULL AND [LastName] IS NOT NULL;

Mapeo de direcciones IP

La clase estándar .NET IPAddress ahora se asignará automáticamente a una columna de cadena para bases de datos que aún no tienen soporte nativo. Por ejemplo, considere mapear este tipo de entidad:

public class Host{
    public int Id { get; set; }
    public IPAddress Address { get; set; }
}

El resultado en SQL:

CREATE TABLE [Host] (
    [Id] int NOT NULL,
    [Address] nvarchar(45) NULL,
    CONSTRAINT [PK_Host] PRIMARY KEY ([Id]));

Podemos agregar estas nuevas entidades de la siguiente manera:

context.AddRange(
    new Host { Address = IPAddress.Parse("127.0.0.1")},
    new Host { Address = IPAddress.Parse("0000:0000:0000:0000:0000:0000:0000:0001")});

Así se verá la consulta resultante en SQL:

Executed DbCommand (14ms) [Parameters=[@p0='1', @p1='127.0.0.1' (Size = 45), @p2='2', @p3='::1' (Size = 45)], CommandType='Text', CommandTimeout='30']      SET NOCOUNT ON;
      INSERT INTO [Host] ([Id], [Address])      VALUES (@p0, @p1), (@p2, @p3);

Excluir OnConfiguración al scaffolding

Si extendimos de alguna manera en algunas clases parciales OnConfiguring sabemos que no es del todo útil. Ahora por medio de la línea de comandos podemos omitir la generación de OnConfiguring, por ejemplo:

dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer --no-onconfiguring

Bloques de código más simples

Mejora en las consultas que dan como resultado un CASE:

context.Weapons
    .OrderBy(w => w.Name.CompareTo("Marcus' Lancer") == 0)
    .ThenBy(w => w.Id)

Resultado de SQL

Antes

SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId]
FROM [Weapons] AS [w]ORDER BY CASE
    WHEN (CASE
        WHEN [w].[Name] = N'Marcus'' Lancer' THEN 0
        WHEN [w].[Name] > N'Marcus'' Lancer' THEN 1
        WHEN [w].[Name] < N'Marcus'' Lancer' THEN -1
    END = 0) AND CASE
        WHEN [w].[Name] = N'Marcus'' Lancer' THEN 0
        WHEN [w].[Name] > N'Marcus'' Lancer' THEN 1
        WHEN [w].[Name] < N'Marcus'' Lancer' THEN -1
    END IS NOT NULL THEN CAST(1 AS bit)
    ELSE CAST(0 AS bit)END, [w].[Id]");

Ahora

SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId]FROM [Weapons] AS [w]
ORDER BY CASE
    WHEN ([w].[Name] = N'Marcus'' Lancer') AND [w].[Name] IS NOT NULL THEN CAST(1 AS bit)
    ELSE CAST(0 AS bit)END, [w].[Id]");

Conclusión

Esta nueva preview tiene muchas, muchas novedades. Alguna me quedó afuera, por eso, te invito a probarlas estas y ver todas las que faltan. En próximos post veremos mas de las novedades de .Net 5.

Fernando Sonego

Deja un comentario

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