SetCompatibilityVersion()
que veíamos en la plantilla de proyectos ASP.NET Core MVC y Razor Pages desde la versión 2.1:public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
Esta llamada era incluida de serie en los nuevos proyectos ASP.NET Core desde la versión 2.1, pero en la versión 3.0 ya no aparece. Y probablemente también os llame la atención a quienes ya habéis trabajado con ASP.NET Core 2.x, así que he pensado que sería interesante comentarlo por aquí.Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: aspnetcore, aspnetcoremvc, novedades
IEnumerable
con un bucle foreach
, cada uno de los elementos debía existir previamente en la colección o bien ser generado de forma síncrona.Por ejemplo, en el siguiente código no teníamos una forma razonable de implementarlo si la obtención de cada uno de los valores retornados desde el método generador tuviera que ser asíncrona:
foreach (var i in GetNumbers())
{
Console.WriteLine(i);
}
IEnumerable<int> GetNumbers()
{
for (var i = 0; i < 1000_000_000; i++)
{
var a = i * 2; // <-- Esto es una operación síncrona,
yield return a; // ¿cómo haríamos si en lugar de esta operación síncrona
// necesitásemos hacer una llamada asíncrona para obtenerlo?
}
}
Aunque convertir el método GetNumbers()
en asíncrono pudiera parecer una alternativa razonable, en realidad no lo es; de hecho, los resultados no llegarían al cliente hasta que hubiéramos generado todos los valores, por lo que sería peor que la primera opción en términos de rendimiento y ocupación de memoria:foreach (var i in await GetNumbersAsync())
{
Console.WriteLine(i);
}
async Task<IEnumerable<int>> GetNumbersAsync()
{
var list = new List<int>();
for (var i = 0; i < 1000_000_000; i++)
{
var a = await Task.FromResult(i * 2); // <-- Aquí generamos los valores usando asincronía,
list.Add(a); // pero el consumidor seguirá esperando hasta
// que los hayamos generado todos.
}
return list; // <-- Aquí retornamos la colección completa
}
En este último caso la llamada a GetNumbersAsync()
se ejecutaría de forma asíncrona, es decir, daríamos la oportunidad al hilo de ejecución actual de dedicarse a otros menesteres mientras la llamada es realizada, desde el punto de vista de su consumidor es a todos los efectos como si se tratara de un método síncrono.Pues bien, aparte de características mainstream como la implementación por defecto en interfaces, los tipos referencia anulables, índices y rangos o muchas otras, en la última versión del framework y C# 8 se ha introducido el soporte para la generación y consumo de secuencias asíncronas.
Para ponernos en situación, imaginemos que tenemos una expresión como la siguiente, donde retornamos el texto
"Rojo"
cuando le suministramos el valor de enumeración Color.Red
, y "Desconocido"
en otros casos. Algo fácil de solucionar utilizando el operador condicional ?
:enum Color { Purple, Red, Blue, Orange, Black, Pink, Gray, Green, White };
string GetColorName(Color color)
{
var str = color == Color.Red ? "Rojo" : "Desconocido";
return str;
}
Imaginemos ahora que la aplicación evoluciona y debemos añadir otro caso a esta condición, como el soporte para el color azul. No pasa nada, podemos seguir el mismo patrón, aunque empezaremos a notar que esto no va a escalar demasiado porque la legibilidad empieza a resentirse:var str = color == Color.Red ? "Rojo" : color == Color.Blue ? "Azul" : "Desconocido";
Bueno, la cuestión es que en ASP.NET Core 3.0 ha vuelto a cambiar, y esperemos que sea por última vez ;)
Veamos en qué han consistido estos cambios.
- .NET Core 3.0
- C# 8
- ASP.NET Core 3.0
- Blazor server-side
- Entity Framework Core 3.0
- Entity Framework 6.3 (sí, ¡compatible con .NET Core!)
- SignalR 3.0
- ML.NET
- Soporte WinForms y WPF para .NET Core 3
- Visual Studio 2019 16.3
- Simplificación del archivo de proyecto
.csproj
- Uso del host genérico
- Introducción del endpoint routing
- Mayor modularidad en el registro de servicios de MVC
- Nuevo serializador/deserializador JSON (bye bye, JSON.NET!)
- Compatibilidad exclusivamente con .NET Core (bye bye, target .NET Framework!)
- Limpieza de
Microsoft.AspNetCore.App
- Cambios en la compilación de vistas
- Soporte para gRPC
- Y, por supuesto, muchas otras mejoras...
Novedades de ASP.NET Core 3.0
¡No os lo perdáis!
Publicado en: www.variablenotfound.com.
Publicado por José M. Aguilar a las 8:55 a. m.
Etiquetas: aspnetcore, aspnetcoremvc, noticias, novedades
Pues sí, y creo que no del todo, respectivamente ;)
En este post vamos a echar un primer vistazo a la que creo que es una de las características más controvertidas de la nueva versión del lenguaje.
Nota: aún estamos usando compiladores y tooling preliminar, por lo que lo dicho aquí podría resultar incompleto o inexacto cuando la versión definitiva de C# 8 sea lanzada (en pocos días, vaya ;)
De hecho, en el top ten de errores de ejecución de aplicaciones creadas con casi cualquier lenguaje y plataforma, las excepciones o crashes debidos a las referencias nulas son, con diferencia, el tipo de error más frecuente que solemos encontrar.
Pues en este repaso que vamos dando a las novedades principales de C# 8, hemos llegado la que probablemente podría ser la característica más destacada en este entrega, cuyo objetivo es precisamente establecer las bases para que podamos olvidarnos de las referencias no controladas a nulos.
No olvidéis que hasta que sea lanzado oficialmente C# 8, para poder probar sus características hay que hacer algunas cosillas.
Recordad que C#8 está aún en preview, y para usarlo hay que seguir los pasos que vimos en un post anterior.En otras palabras, esta mejora pretende simplificar implementaciones como las siguientes, donde comprobamos si una variable contiene
null
y, en caso afirmativo, le asignamos un valor:...
var defaultValue = ... // lo que sea;
var x = GetSomething();
// Usando un bloque if:
if(x == null)
{
x = defaultValue;
}
// O bien, usando el null coalescing operator:
x = x ?? defaultValue;
Muchas veces estas sentencias SQL generadas de forma automática y ejecutadas al servidor son fáciles de leer y entender, pero hay veces que EF traduce el LINQ a consultas SQL enormes, complejas, con escasa legibilidad y difícilmente reconocibles.
Seguro que alguna vez habéis tenido por delante una de estas complejas sentencias SQL generada por Entity Framework y os hubiera gustado saber en qué punto del código fue lanzada. Esto es muy frecuente, por ejemplo, cuando estamos monitorizando las consultas en ejecución con SQL Profiler, o al examinar las queries que consumen mayor número de recursos desde los paneles de Azure SQL.
En versiones "clásicas" de Entity Framework había que ingeniárselas para conseguirlo, pero, como podréis comprobar a continuación, en EF Core la cosa se ha simplificado bastante :)
Index
, junto con alguna cortesía del compilador, permitía la especificación de índices en arrays de forma muy sencilla. Veíamos cómo podíamos acceder a elementos concretos utilizando su posición en la colección, tanto contando desde el principio como desde el final:var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };
Index fromStart = 2; // = Index.FromStart(2) - conversión implícita
Index fromEnd = ^2; // = Index.FromEnd(2)
Console.WriteLine(primes[fromStart]); // 5
Console.WriteLine(primes[fromEnd]); // 17
Sin embargo, puede que a Index
por sí mismo tampoco le veáis demasiada utilidad... y así es. De hecho, su finalidad es más bien el dar soporte a rangos, una nueva característica de C#8 que nos permitirá referirnos a "porciones" de arrays o colecciones similares usando una sintaxis compacta e integrada en el lenguaje.Span<T>
.Como muchas otras características del lenguaje, se trata de algunos azucarillos sintácticos creados en torno a dos nuevos tipos añadidos a las bibliotecas básicas del framework: las estructuras
System.Index
y System.Range
. Por esta razón, para utilizar estos elementos no sólo es necesario disponer de nuevos compiladores, sino también de nuevas versiones del framework.Recordad que a día de hoy ya se puede probar C# 8 en Visual Studio 2019 o directamente desde la interfaz de línea de comandos de .NET Core.
using
, utilizada tanto en forma de directiva como de instrucción, es una de las más sobrecargadas del lenguaje C#. Es útil para bastantes cosas, como la importación de espacios de nombres, definición de alias de namespaces o tipos, simplificar el acceso a miembros de tipos estáticos, o para especificar bloques o ámbitos de uso de recursos (objetos IDisposable
) que deben ser liberados automáticamente.Centrándonos en este último caso de uso, seguro que en muchas ocasiones habéis escrito código como el siguiente, donde vamos anidando objetos
IDisposable
para asegurar que al finalizar la ejecución de cada bloque los recursos sean liberados de forma automática:void DoSomething()
{
using(var conn = new SqlConnection(...))
{
connection.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "...";
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
// ...
}
}
}
}
}
Al final, lo que encontramos es código con un nivel de indentación muy alto, y que resulta muy extenso, básicamente porque una gran parte de las líneas las dedicamos sólo a abrir y cerrar llaves. A la postre, esto sólo hace que nuestro código crezca a lo ancho, lo cual no es bueno desde el punto de vista de la simplicidad y facilidad de lectura.Por ejemplo, en las consultorías técnicas que realizo en empresas es frecuente encontrar equipos de trabajo en los que aún no está generalizado el uso de construcciones tan útiles como el null coalescing operator (
fullName ?? "Anonymous"
), safe navigation operator (person?.Address?.Street
), el at object operator (Address@person
), o características tan potentes como las funciones locales, interpolación de cadenas, tuplas o muchas otras.Sin embargo, creo que el rey de los desconocidos es el operador virgulilla "~" de C#. Introducido con C#7 es probablemente uno de los operadores menos utilizados y, sin embargo, de los más potentes ofrecidos por el lenguaje.
Nota de traducción: el nombre original del operador es "tilde operator", y como he encontrado poca literatura al respecto en nuestro idioma, me he tomado la libertad de traducirlo como operador virgulilla (¡sí, esa palabra existe!). También, en entornos más informales lo encontraréis con el nombre "wormy operator" (operador gusanillo) o como "soft similarity operator" (que podríamos traducir como operador de similitud relajada).
En todos los casos son revisiones pequeñas y que no rompen nada de lo anterior, pero en cada uno de estos productos se han introducido mejoras que vale la pena conocer, por lo que, antes que nada, os recomiendo que echéis un vistazo a los artículos anteriores, que son los anuncios oficiales.
En este post vamos a ver rápidamente las novedades más destacables de ASP.NET Core 2.2.
ASP.NET Core 2.1 continúa profundizando en esa línea e incluye entre sus novedades el nuevo atributo
[ApiController]
, un decorador aplicable a controladores que los identifica como puntos de entrada de APIS, aplicando de forma automática una serie de convenciones bastante útiles a la hora de crear este tipo de componentes:[ApiController]
[Route("api/[controller]")]
public class ValuesController : ControllerBase
{
...
}
Fijaos que, a diferencia de ASP.NET Web API (.NET Framework), se ha optado por utilizar un atributo en lugar de emplear herencia (en aquél framework existía la clase ApiController
).
A continuación veremos qué debemos tener en cuenta a la hora de aplicar este atributo a nuestros controladores y qué convenciones son las que estaremos asumiendo al utilizarlo.
Publicado por José M. Aguilar a las 8:55 a. m.
Etiquetas: aspnetcore, aspnetcoremvc, novedades, webapi
ref
. Aunque su alcance era algo limitado, nos permitía coquetear con punteros para cubrir de forma eficiente algunos escenarios y permitirnos algunos usos avanzados, pero sin necesidad de abandonar la seguridad que nos ofrece el entorno de ejecución de .NET.Un ejemplo clásico es el uso de
ref
para intercambiar dos valores desde un método:int one = 1, two = 2; Swap(ref one, ref two); Console.WriteLine($"{one},{two}"); // 2,1 ... void Swap<T>(ref T a, ref T b) { var temp = a; a = b; b = temp; }En C#7, el ámbito de uso de las referencias se ha ampliado bastante gracias a la introducción de dos nuevas características en el lenguaje:
- El soporte para variables locales de tipo referencia, o ref locals.
- La capacidad de un método o función de retornar referencias, también conocida como ref returns.
Veamos en qué consisten.
En esta ocasión veremos un par de pequeñas adiciones al lenguaje que, aunque de mucho menor calado de otras que ya hemos repasado, también merecen tener su minutillo de protagonismo ;)
En realidad no es algo demasiado diferente a lo que hacemos normalmente cuando almacenamos en una variable local el resultado de un método que retorna un único valor:
// Guardamos el valor de retorno en variable local var sum = Sum(1, 3); Console.WriteLine($"Sum: {sum}"); ... static int Sum(int a, int b) { return a+b; }
Fijaos en un código como el siguiente, que seguro que habéis escrito cientos de veces, donde utilizamos el constructor de una clase para recibir sus dependencias y almacenarlas en miembros de la instancia:
public class MyService: IMyService { private readonly IDependency _first; private readonly IAnotherDependency _second; public MyService(IDependency first, IAnotherDependency second) { if (first==null) throw new ArgumentNullException("first"); if (second == null) // O mejor, usando el operador nameof throw new ArgumentNullException(nameof(second)); _first = first; _second = second; } ... }
En esta ocasión nos centraremos en la vuelta de tuerca que se ha dado a las tuplas a nivel de lenguaje, reforzándolas como first-class citizens para los desarrolladores C#.