Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

18 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
Mostrando entradas con la etiqueta trucos. Mostrar todas las entradas
Mostrando entradas con la etiqueta trucos. Mostrar todas las entradas
martes, 21 de junio de 2022
.NET

De casualidad me he topado con un interesante cambio que .NET 5 introdujo en los componentes de serialización y deserialización System.Text.Json y que, al menos para mí, pasó totalmente desapercibido en su momento. Por eso, he pensado que quizás sea buena idea dejarlo por aquí, por si hay algún despistado más al que pueda resultar útil.

Como seguro sabéis, al usar los componentes de System.Text.Json para serializar o deserializar una clase, utilizamos el atributo [JsonIgnore] para marcar las propiedades que queremos que sean ignoradas al convertir desde y hacia JSON.

Por ejemplo, dada la siguiente clase:

class User
{
    public int Id { get; set; }
    public string Email { get; set; }
    [JsonIgnore]
    public string Token { get; set; }
}

En ella estamos indicando expresamente que la propiedad Token debe ser ignorada, por lo que ésta no aparecerá si serializamos un objeto a JSON:

var user = new User { Id = 42, Email = "john@server.com", Token = "ABCDEF"};
Console.WriteLine(JsonSerializer.Serialize(user));

// Result:
{"Id":42,"Email":"john@server.com"}

Y lo mismo ocurre en sentido contrario:

var jsonStr = "{ \"Id\": 42, \"Email\": \"john@server.com\", \"Token\": \"ABCDEF\"}";
var user = JsonSerializer.Deserialize<User>(jsonStr);
// ¡user.Token es nulo aquí!
martes, 14 de junio de 2022
.NET

Visual Studio sigue introduciendo novedades versión tras versión, y es fácil que algunas de ellas nos pasen desapercibidas y tardemos algún tiempo en conocerlas, o incluso en verles la utilidad. Un ejemplo lo tenemos en los breakpoints temporales y dependientes, dos nuevos tipos de puntos de interrupción añadidos en la versión 2022 que pueden venirnos bien a la hora de depurar aplicaciones.

En este post vamos a echarles un vistazo, por si hay algún despistado más que no se haya dado cuenta de estas novedades.

martes, 7 de junio de 2022
.NET

Las top level statements o instrucciones de nivel superior de C# 9 introdujeron una alternativa muy concisa para implementar los entry points de nuestras aplicaciones. De hecho, en .NET 6 fueron introducidas como la opción por defecto en las plantillas, por lo que, de alguna forma, se nos estaba forzando a utilizarlas en todos los nuevos proyectos.

Y como casi siempre sucede, rápidamente aparecieron numerosos desarrolladores a los que este cambio no les había hecho nada de gracia, y se manifestaron claramente en contra de que esta fuera la opción por defecto. La decisión por parte de los equipos de Visual Studio y .NET, que ya podemos ver si tenemos las últimas actualizaciones instaladas, es dejar que cada desarrollador decida la opción que más le guste.

martes, 31 de mayo de 2022
.NET

A veces, desde aplicaciones .NET de consola, escritorio, o incluso ASP.NET Core, puede resultar interesante conectarse con una hoja de Google Sheets para añadir filas de datos.

Hay varias formas de conseguirlo, pero aquí vamos a ver la que creo que es la más sencilla, pues permite evitar parte del engorroso workflow de OAuth y, lo que es mejor, podemos usarla sin necesitar credenciales de usuario desde, por ejemplo, un servidor o un proceso desasistido.

Ojo: las APIs de Google que vamos a ver son gratuitas, pero tienen limitaciones de uso que debéis conocer antes de utilizarlas.

A grandes rasgos, el proceso consta de los siguientes pasos, que seguiremos a lo largo del post:

  • Configuración del proyecto y credenciales en Google Developer Console.
  • Creación del documento Google Sheet en el que añadiremos las filas.
  • Consumo de las APIs de Google para añadir datos.

¡A por ello!

martes, 10 de mayo de 2022
.NET

Solemos pensar que el punto de entrada de una aplicación .NET, ya sea el método estático Main() del clásico Program.cs o el nuevo Program.cs de .NET 6 que utiliza top level statements, es lo primero que se ejecuta en nuestras aplicaciones, en realidad no es así. Hay vida antes de que nuestra aplicación empiece a correr ;)

Aunque obviamente su utilidad es limitada y no es algo que necesitaremos hacer a menudo (o probablemente nunca), es conveniente saber que existen varias formas de insertar código que se ejecute antes que lo que siempre hemos considerado el entry point de nuestra aplicación, y es lo que veremos en este post.

martes, 26 de abril de 2022
ASP.NET Core

Imaginad que tenemos un controlador MVC como el siguiente:

public class TestController : Controller
{
    public IActionResult Add(int a, int b)
    {
        return Content($"Result: {a + b}");
    }
}

Claramente, la acción Add() retornará la suma de los enteros a y b que le llegarán como parámetros de la query string:

GET https://localhost:7182/test/add?a=1&b=2 HTTP/1.1

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8

Result: 3

Pero, como sabemos, podríamos llamar a la acción sin indicar alguno de esos parámetros, o incluso ninguno de ellos:

Petición Respuesta
GET /test/add?a=1&b=2 Result: 3
GET /test/add?a=0&b=0 Result: 0
GET /test/add?a=1 Result: 1
GET /test/add Result: 0

Esto es así porque el binder será capaz de poblar correctamente los parámetros a y b cuando estén presentes en la cadena de la petición y sean correctos, o les asignará su valor por defecto (0) cuando no hayan sido suministrados.

Pero dado que el cero es un valor de entrada válido, a priori desde nuestro código no tendríamos forma de distinguir cuándo el parámetro ha sido omitido y cuándo se ha establecido expresamente.

¿Cómo podríamos hacerlo?

martes, 19 de abril de 2022
.NET

La semana pasada veíamos algunas alternativas para comprobar de forma eficiente si una cadena de texto contiene un JSON válido y, al hilo de dicho post, el amigo Javier Campos aportó vía Twitter una fórmula adicional mejor que las que vimos aquí.

El código en cuestión es el siguiente:

public bool IsJsonWithReader(string maybeJson)
{
    try
    {
        var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(maybeJson));
        reader.Read();
        reader.Skip();
        return true;
    }
    catch
    {
        return false;
    }
}

La estructura Utf8JsonReader ofrece una API de alto rendimiento para acceder en modo forward-only y read-only al JSON presente en una secuencia de bytes UTF8. Los métodos Read() y Skip() se encargan respectivamente de leer el primer token del JSON y saltarse todos sus hijos, con lo que en la práctica se recorrerá el documento completo, lanzándose una excepción en caso de ser inválido.

martes, 5 de abril de 2022
.NET

Al hilo del post Cómo recibir un JSON como string en una acción ASP.NET Core MVC, el amigo Alberto dejaba una interesante pregunta en los comentarios: ¿y si una vez hemos recibido el string, queremos validar que sea un JSON válido?

Obviamente, una forma sencilla sería intentar deserializarlo por completo a la clase de destino, siempre que ésta sea conocida. Para ello podríamos utilizar el método Deserialize<T>() del objeto JsonSerializer de System.Text.Json, disponible en todas las versiones modernas de .NET, de la siguiente manera:

martes, 29 de marzo de 2022
ASP.NET Core

A veces, sobre todo en aplicaciones muy grandes, con las definiciones de rutas muy complejas o cuando nos toca analizar aplicaciones ajenas, puede ser interesante saber qué punto del código está procesando una petición determinada, ya sea un endpoint definido usando Endpoint Routing o Minimal APIs o bien sea una acción de un controlador MVC.

En este post vamos a ver cómo conseguirlo de forma muy sencilla, mediante la implementación un pequeño middleware que, insertado en el pipeline, añadirá en el encabezado información sobre el handler que generó la respuesta de la petición actual.

martes, 22 de marzo de 2022
.NET

Aunque no es algo frecuente ni especialmente recomendable, hay veces que tenemos que introducir en el código algún tipo de lógica, trazas o comprobaciones que solo queremos aplicar mientras depuramos o desarrollamos y, en ningún caso, queremos que vaya a producción.

En estos casos, aparte de anudarnos un lazo en el dedo mientras mantengamos estos cambios para asegurar la eliminación del código de pruebas antes de subir la aplicación a producción, otra idea puede ser utilizar directivas para que este código solo actúe mientras depuramos, o incluso para evitar la compilación exitosa en modo "Release" (el usado normalmente al publicar).

martes, 15 de marzo de 2022
ASP.NET Core

En los tiempos de ASP.NET "clásico", cuando los settings de la aplicación los almacenábamos en el viejo Web.config, cualquier intento de cambio de valores mientras ejecutábamos la aplicación era inevitablemente sinónimo de reinicio. Esto, aunque bastante molesto, tenía sentido porque el mismo archivo XML se utilizaba para almacenar tanto los valores de configuración "de negocio" como aspectos puramente técnicos o del comportamiento de la infraestructura de ASP.NET, y la aplicación debía reiniciarse para poder aplicarlos.

Con la llegada del sistema de settings de .NET Core esto mejoró bastante, introduciendo la posibilidad de almacenar valores en bastantes orígenes distintos (archivos .json, .ini, .xml, variables de entorno, parámetros de línea de comandos, user secrets, diccionarios en memoria y muchos otros, incluso totalmente personalizados), y nuevas fórmulas para la obtención de éstos, como la inyección de dependencias, settings tipados y, por fin, la posibilidad de realizar cambios en caliente.

martes, 8 de febrero de 2022
ASP.NET Core MVC

Dada una petición como la siguiente:

POST https://localhost:5001/friends HTTP/1.1
Host: localhost:5001
Content-Type: application/json

{ "name": "Jon", "age": 24 }

Lo habitual es que queramos recibir los datos ya materializados en forma de objeto de .NET. Esto podemos conseguirlo fácilmente desde una acción como la siguiente:

[Route("friends")]
public class FriendsController : Controller
{
    [HttpPost]
    public ActionResult Test([FromBody]Friend friend)
    {
        return Content($"Hello, {friend.Name}, you are {friend.Age} years old");
    }
}

Pero aunque no es algo que ocurra con frecuencia, a veces podríamos necesitar recibir el cuerpo JSON de una petición como string, es decir, en crudo, sin deserializarlo ni procesarlo de ninguna manera para, ya más adelante, hacerlo nosotros manualmente. Y aunque a primera vista pudiera resultar trivial, tiene un poco más de truco de lo que parece...

martes, 1 de febrero de 2022
Blazor

Si habéis utilizado isolation con módulos JavaScript en Blazor 5, probablemente lo que vamos a ver en este post os resulte muy interesante para organizar los archivos JavaScript de la aplicación, especialmente aquellos que sean utilizados desde un único componente.

Como recordaréis, el aislamiento de JavaScript de Blazor permitía cargar de forma dinámica un archivo .js y ejecutar código definido en él. Por ejemplo, imaginad un módulo como el siguiente, definido en wwwroot/modules/MyPrompt.js:

export function showPrompt(message, defaultValue) {
    return prompt(message, defaultValue);
}

martes, 18 de enero de 2022
.NET

Versiones de .NET anteriores a la 6 no disponían de una fórmula específica para determinar si un tipo o interfaz está registrado como servicio en el sistema de inyección de dependencias.

La única forma de hacerlo era intentar resolverlo, usando métodos como GetService() o GetService<T>(), y comprobar si el resultado era null:

var myService = serviceProvider.GetService<IMyService>();
if(myService is null)
{
    // El servicio no está registrado, hacemos algo
}

¿Cuál es el inconveniente de esto? Si el servicio no está registrado, ninguno: la llamada retornará un nulo y listo. 

El problema viene cuando sí está registrado, pues estaremos forzando la resolución de un servicio que, en realidad, no necesitamos para nada, pues sólo nos interesaba saber si existía o no. Porque recordemos que la resolución de un servicio podría tener un coste importante en términos de rendimiento, memoria, o incluso efectos colaterales en el estado de la aplicación, especialmente si nuestro servicio depende de otros, que a su vez dependen de otros, etc.

martes, 15 de junio de 2021
.NET Core

No hace demasiado tiempo he descubierto en el framework la clase ActivatorUtilities, una pequeña joyita disponible en Microsoft.Extensions.DependencyInjection, que puede resultar interesante para implementar fácilmente factorías o cualquier tipo de código donde tengamos que instanciar manualmente clases con dependencias hacia servicios registrados en el contenedor de nuestra aplicación.

La clase estática ActivatorUtilities ofrece básicamente tres métodos públicos:

  • CreateFactory(), para crear factorías de objetos.
  • CreateInstance() / CreateInstance<T>(), para crear instancias de objetos.
  • GetServiceOrCreateInstance() / GetServiceOrCreateInstance<T>(), para obtener una instancia desde el inyector, o bien crearla manualmente con CreateInstance().
Veámoslos en mayor detalle.
martes, 8 de junio de 2021
Blazor

Blazor permite crear componentes que son ignorantes respecto al modelo de hosting que están utilizando en tiempo de ejecución. Es decir, si lo diseñamos apropiadamente, un componente podría funcionar indistintamente en Blazor Server y Blazor WebAssembly, lo cual sería ideal desde el punto de vista de su reutilización.

Sin embargo, desde el interior de estos componentes, a veces necesitaremos distinguir cuándo están siendo ejecutados en Blazor Server y cuándo en Blazor WebAssembly. ¿Cómo podemos conseguir esto? Pues tenemos muchas maneras...

martes, 18 de mayo de 2021
Blazor

Durante la implementación de páginas o componentes Blazor en archivos .razor, es relativamente frecuente encontrarse con casos en los que nos interesa reutilizar un bloque de código Razor en más de un punto. 

Por ejemplo, observad el siguiente ejemplo:

<h1>Todo list</h1>
<h2>Pending tasks</h2>
<ul>
    @foreach (TodoItem item in TodoItems.Where(i=>!i.IsDone).OrderBy(i=>i.Priority))
    {
        <li>@item.Task, owned by @item.Owner and created at @item.CreatedAt</li>
    }
</ul>
<h2>Finished tasks</h2>
<ul>
    @foreach (TodoItem item in TodoItems.Where(i=>i.IsDone).OrderBy(i=>i.DateFinished))
    {
        <li>@item.Task, owned by @item.Owner and created at @item.CreatedAt</li>
    }
</ul>

En este componente, podemos ver claramente que estamos repitiendo los dos bloques de código encargados de mostrar los elementos de cada una de las listas, por lo que, si en el futuro quisiéramos cambiar la forma de mostrar un TodoItem, tendríamos que modificar el interior de los dos bloques. Es frecuente en estos casos optar por crear un nuevo componente que se encargue de ello, por ejemplo, llamado TodoListItem:

<li>@Item.Task, owned by @Item.Owner and created at @Item.CreatedAt</li>
@code {
    [Parameter]
    public TodoItem Item { get; set;}
}

De esta forma ya tendremos el código de renderización del TodoItem centralizado y podremos simplificar el bloque anterior eliminando la duplicidad:

<h1>Todo list</h1>
<h2>Pending tasks</h2>
<ul>
    @foreach (TodoItem item in TodoItems.Where(i=>!i.IsDone).OrderBy(i=>i.Priority))
    {
        <TodoListItem Item="item" />
    }
</ul>
<h2>Finished tasks</h2>
<ul>
    @foreach (TodoItem item in TodoItems.Where(i=>i.IsDone).OrderBy(i=>i.DateFinished))
    {
        <TodoListItem Item="item" />
    }
</ul>

Aunque conceptualmente la solución que hemos implementado es correcta, introduce un problema en nuestra aplicación: por el mero hecho de querer evitar la duplicación de código, estamos introduciendo en la página un número indeterminado de componentes, lo cual podría afectar drásticamente a su rendimiento.

Por llevarlo al extremo, imaginad que esas listas tienen miles de elementos. En este caso, en nuestra página estaríamos introduciendo miles de componentes, con lo que esto implica:

  • Deberían instanciarse miles de componentes (objetos).
  • Deberían ejecutarse los eventos del ciclo de vida de cada componente al crearlos, inicializarlos, renderizarlos, etc.
  • Mientras se encuentren en la página cada componente ocuparía memoria, ya sea en cliente (Blazor WebAssembly) o en servidor (Blazor Server).

Esto podría llegar incluso a hacer una página inutilizable, por lo que es importante disponer de otros métodos para crear y reutilizar bloques de código HTML sin necesidad de crear componentes. Esta es una de las utilidades de los render fragments.

martes, 4 de mayo de 2021
Logging

Hace unos días pasaba a un amigo una instrucción de PowerShell para visualizar en tiempo real el contenido que iba añadiéndose al final de un archivo de log en Windows, y pensé que igual podía ser útil para alguien más, así que aquí va :)

El asunto es tan simple como abrir un terminal o consola PowerShell en la carpeta donde tengamos el archivo y ejecutar la siguiente orden:

Get-content log.txt -Tail 0 -Wait

A partir de ese momento, la consola quedará bloqueada e irá mostrando en tiempo real las últimas líneas añadidas al archivo:

Ventana de consola mostrando las últimas líneas añadidas a un archivo de texto

Esta idea tan sencilla podría ser combinada en scripts que nos simplifiquen alguna tarea más; por ejemplo, si estamos en una carpeta con archivos de trazas de distintos días, es sencillo conseguir que se abra el fichero de log más reciente, de forma que no tengamos que introducir su nombre cada vez que queramos utilizarlo:

# File: ViewLog.ps1
$file = Get-ChildItem -Filter *.txt | Sort-Object LastAccessTime -Descending | Select-Object -First 1
if($file) 
{
	Get-content $file -Tail 0 -Wait
}

¡Espero que os sea útil!

Publicado en Variable not found.

martes, 23 de febrero de 2021
.NET Core

Ya hace tiempo que se lanzó .NET 5, pero seguro que algunos os habéis dado cuenta de que cuando desde Visual Studio creamos determinados tipos de proyecto, como bibliotecas de clases o proyectos de consola, por defecto éstos utilizan como target .NET Core 3.1 en lugar de .NET 5.

No se trata de un error; desde Microsoft justifican esta decisión porque .NET 5 no es una versión LTS, y prefieren que por defecto los proyectos sean creados usando una versión con mayor tiempo de soporte, como .NET Core 3.1.

Esto tiene fácil solución, porque tras crearlo simplemente deberíamos acceder a las propiedades del proyecto o editar el archivo .csproj y modificar ajustar el target framework a nuestro antojo, pero, ¿cómo podríamos evitar tener que hacer esto cada vez?

martes, 15 de diciembre de 2020
Blazor

En Blazor, es habitual implementar la interfaz de nuestros componentes de forma declarativa, mezclando etiquetas que:

  • Pueden ser HTML, y cuyo resultado irá textualmente al navegador
  • Pueden ser instancias de otros componentes, por lo que al navegador irá el resultado de la renderización de éstos.

Por ejemplo, el siguiente código envía al navegador la etiqueta <h1> y luego el resultado de renderizar el componente Blazor <MyComponent>, en cuyo interior podrán existir a su vez otras etiquetas o componentes:

@page "/home"
<h1>Welcome!<h1>
<MyComponent />

Fijaos que, aunque es la fórmula que emplearemos en la mayoría de ocasiones, es una estructura totalmente rígida: podemos tener la total certeza de que el componente que será instanciado en la página anterior es MyComponent, pero, ¿qué ocurre si queremos poder decidir en tiempo de ejecución qué componente instanciaremos en la página?