En toda actualización de .Net, se introducen novedades en los lenguajes, y en este contexto, se destacan los avances en C#, que constituye el lenguaje principal.
De la mano de .Net 6, se presenta la versión 10 de C# con diversas mejoras, como rendimiento, hot reload, Minimal API y significativas innovaciones en la construcción de aplicaciones móviles. Aunque en cada lanzamiento se centra en las novedades principales, muchas veces otras pasan desapercibidas para la mayoría de los programadores.
Quizás las mejoras que observemos no sean revolucionarias ni cambien drásticamente nuestra vida, pero sí cambiarán nuestra perspectiva sobre cómo realizar las tareas. En esta nueva versión, se incorporan diversas funcionalidades; por ejemplo, la capacidad de eliminar repeticiones, facilitando la creación de código de alta calidad.
Nueva API en LINQ para evitar cortar colecciones grandes de forma manual
Cuando manejamos grandes conjuntos de datos, es poco frecuente que utilicemos el conjunto completo. Siempre nos movemos con algún fragmento más reducido. Como mencionamos, la idea de fragmentar esta cantidad de registros a través de páginas es lo más práctico, pero no siempre está disponible para implementarlo.
¿Cuál sería la aproximación para esto? Podemos emplear métodos de Linq con las funciones Take y SKIP, o podemos crear una extensión en Linq para establecer una manera de fragmentar estos registros y organizarlos en páginas.
static class LinqExtensions
{
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
{
int i = 0;
var splits = from item in list
group item by i++ % parts into part
select part.AsEnumerable();
return splits;
}
}
Ya no es necesario llevar a cabo esta tarea. En Linq, ahora contamos con el método Chunk, que nos facilita dividir en varias páginas una colección grande. Solo es necesario especificar la cantidad de registros que se cortan por página.
int pageSize = 10;
IEnumerable<Employee[]> employeeChunk = employees.Chunk(pageSize);
Para fecha solamente tenemos DateOnly
Siempre que es necesario lidiar con fechas y horas, recurrimos a la clase DateTime. Pero, ¿qué sucede si solo queremos manejar la fecha? Ahora contamos con la estructura DateOnly, y también hay TimeOnly, para la hora, minutos y segundos. Veamos cómo lo abordamos anteriormente:
var _date = new DateTime(2022, 1, 24);
var _theDate = _date .ToShortDateString();
Console.WriteLine(_date ); // 24/01/2022 12:00:00 AM
Console.WriteLine(_theDate ); // 24/01/2022
Cómo usamos DateOnly:
var _date = new DateOnly(2022, 1, 24);
Console.WriteLine(_date); // 24/01/2022
Hay otras alternativas nuevas para trabajar con estas fechas, como calcular la diferencia de días entre fechas o realizar sumas.
Nuevo MiddleWare para registro de actividad
Hasta la versión .Net 6, el proceso de registrar datos de una solicitud HTTP resultaba bastante tedioso y laborioso. Teníamos la opción de crear un filtro que incorporamos al pipeline de mediciones y registrarlo en nuestro archivo startup.
No es necesario llevar a cabo todo lo mencionado anteriormente en esta versión; podemos utilizar el nuevo middleware:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpLogging();
}
También tenemos la posibilidad de personalizar lo que deseamos registrar.
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpLogging(logging =>
{
logging.LoggingFields = HttpLoggingFields.All;
logging.RequestHeaders.Add("X-Request-Header");
logging.ResponseHeaders.Add("X-Response-Header");
logging.RequestBodyLogLimit = 4096;
logging.ResponseBodyLogLimit = 4096;
});
}
Componente ErrorBoundary en Blazor
Cuando se produce una excepción no controlada en Blazor Server, se maneja un error fatal que advierte que “el circuito queda en un estado indefinido que podría ocasionar problemas de estabilidad o seguridad en Blazor Server”. Es probable que incorporamos extensamente bloques try/catch en nuestro código como medida preventiva o encapsular la lógica en JavaScript, ya que no es código C#.
Para evitar esta situación, contamos con el nuevo componente ErrorBoundary. Aunque no actúa como un controlador de excepciones global, contribuirá a gestionar el comportamiento impredecible, especialmente con componentes que no maneja ni puede manejar.
<div class="main">
<div class="content px-4">
<ErrorBoundary>
@Body
</ErrorBoundary>
</div>
</div>
También es posible emplearlos en cualquier ubicación y personalizar el manejo de errores.
<tbody>
@foreach (var employee in Employees)
{
<ErrorBoundary @key="@board">
<ChildContent>
<tr>
<td>@employee.Id</td>
<td>@employee.FirstName</td>
<td>@employee.LastName</td>
</tr>
</ChildContent>
<ErrorContent>
Sorry, I can't show @employee.Id because of an internal error.
</ErrorContent>
</ErrorBoundary>
}
</tbody>
Nuevas Subcategorías para Server.Kestrel
Si queremos habilitar el registro detallado para Kestrel, antes debíamos utilizar Microsoft.AspNetCore.Server.Kestrel. Aunque sigue existiendo, ahora cuenta con nuevas subcategorías que deberían facilitar las cosas.
Lo clásico es:
{
"Logging": {
"LogLevel": {
"Microsoft.AspNetCore.Server.Kestrel": "Debug"
}
}
}
Mejor hacerlo con la subcategoría:
{
"Logging": {
"LogLevel": {
"Microsoft.AspNetCore.Server.Kestrel.BadRequests": "Debug"
}
}
}
Sin lugar a dudas, esto simplificará enormemente el manejo del registro detallado de Kestrel.
Namespaces de archivo de C# 10
Se trata de una nueva funcionalidad que personalmente encuentro muy útil. Hasta ahora, el espacio de nombres debía estar entre llaves, algo así:
namespace ConsoleApp.Models
{
public class Customer
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
}
}
Ya no es requerido, podemos utilizar namespaces a nivel de archivo:
namespace SuperheroApp.Models;
public class Customer
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
}
Además, si es un objeto pequeño, podríamos utilizar directamente un récord para definir nuestra clase:
namespace Customer.Models;
public record Customer(string? FirstName, string? LastName);
Exceso de brackets
En la sección anterior, exploramos las ventajas de eliminar algunos elementos; no obstante, también podemos abusar de ellos, por ejemplo:
public static int CalculateShirt(Customer customer) =>
customers witch
{
{ Shirt: { Color: "Blue" } } => 100,
{ Shirt: { Color: "Yellow" } } => 200,
{ Shirt: { Color: "Red" } } => 300
_ => 0
};
Mejor es este camino:
public static int CalculateShirt(Customer customer) =>
Customer switch
{
{ Shirt.Color: "Blue" } => 100,
{ Shirt.Color: "Yellow" } => 200,
{ Shirt.Color: "Red" } => 300
_ => 0
};
Conclusiones
Estas son algunas de las innovaciones que a veces no se detallan mucho y que verdaderamente son muy aplicadas en el día a día. En próximas publicaciones explicaremos más aspectos de .Net y C#.