Autor en Google+
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 ;)

16 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 .net. Mostrar todas las entradas
Mostrando entradas con la etiqueta .net. Mostrar todas las entradas
martes, 8 de noviembre de 2022
Compartir:
C#

En código que veo, incluso escrito por mí un tiempo atrás, es muy habitual encontrar comparaciones de cadenas de caracteres en las que, para asegurar que el casing no sea tenido en cuenta, se fuerza una conversión de alguno de los operandos, o incluso ambos, a mayúsculas o minúsculas.

En escenarios muy simples esto funcionará bien y no tendrá contraindicaciones especialmente graves para nuestro sistema. Veamos unos ejemplos:

// Ejemplo 1: conversión de un único operando
int Calculate(string op, int a, int b)
{
    // Pasamos a minúsculas el operador para
    // asegurar que encaja con la constante
    if(op.ToLower()=="add")
    {
        return a+b;
    } 
    else if(op.ToLower()=="sub")
    {
        return a-b;
    }
    ...
}

// Ejemplo  2: conversión de ambos operandos
bool AreBrothers(User user1, User user2)
{
    // Pasamos a mayúsculas ambos apellidos por
    // si alguno se ha escrito usando otro casing
    var areBrothers = user1.Surname.ToUpper() == user2.Surname.ToUpper();
    return areBrothers;
}

Sin embargo, aunque pueda parecer despreciable, estas operaciones de transformación a mayúsculas o minúsculas tienen un coste importante, que se pone especialmente de manifiesto cuando estamos hablando de aplicaciones con mucha carga de usuarios, alojada en infraestructura muy ajustada o cuando se requiere un rendimiento extremo.

Compartir:
martes, 27 de septiembre de 2022
Compartir:
.NET

En un vídeo del canal de Nick Chapsas, al que por cierto os recomiendo suscribiros, he descubierto que .NET 7 introducirá un mecanismo para "decorar" parámetros, propiedades y miembros de tipo string de forma que podamos aportar información sobre el tipo de contenido que esperan almacenar.

Para que lo entendáis mejor, observad el siguiente ejemplo, una función que recibe un mensaje y un formato de fecha, y que escribe por consola la fecha actual en el formato indicado seguido del mensaje.

void Log(string message, string dateFormat)
{
    Console.WriteLine(DateTime.UtcNow.ToString(dateFormat) + " - " + message);
}

Log("Hello!", "dd/MM/yyyy hh:mm");

Desde el punto de vista del consumidor de la función Log(), gracias a las ayudas del IDE podremos deducir que el segundo parámetro de tipo string, llamado dateFormat, debería ser un formato de fecha válido en .NET. Sin embargo, el entorno de desarrollo no podrá ofrecer ningún tipo de ayuda a la hora de codificar la llamada ni detectar si se producen errores, pues no dispone de información suficiente sobre el tipo de contenido esperado en la cadena de texto que se le suministra.

Compartir:
martes, 21 de junio de 2022
Compartir:
.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í!
Compartir:
martes, 31 de mayo de 2022
Compartir:
.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!

Compartir:
martes, 10 de mayo de 2022
Compartir:
.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.

Compartir:
martes, 19 de abril de 2022
Compartir:
.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.

Compartir:
martes, 5 de abril de 2022
Compartir:
.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:

Compartir:
martes, 15 de febrero de 2022
Compartir:
.NET

Seguimos descubriendo novedades aparecidas con .NET 6, y ahora le toca el turno a la nueva clase PeriodicTimer, una fórmula para la ejecución de tareas periódicas en contextos asíncronos que evita el uso de los clásicos callbacks a los que nos tenía acostumbrados el framework.

Como recordaréis, .NET dispone de un buen número de formas de implementar temporizadores, o timers, para ejecutar tareas en intervalos periódicos. El más conocido probablemente sea el clásico System.Threading.Timer, en el que especificábamos el callback o método que debía ejecutarse en cada intervalo de tiempo mediante un delegado (en el siguiente ejemplo, mediante una lambda):

var timer = new System.Threading.Timer(o =>
{
    Console.WriteLine("Hey! " + DateTime.Now.ToLongTimeString());
}, null, 0, 1000);

Console.ReadKey();
Hey! 12:25:51
Hey! 12:25:52
Hey! 12:25:53
Hey! 12:25:54
Hey! 12:25:55
_

Pero también existía System.Timers.Timer, que nos permitía lograr algo parecido, aunque esta el callback lo implementábamos mediante una suscripción al evento Elapsed del objeto:

var timer = new System.Timers.Timer(1000);
timer.Elapsed += (sender, eventArgs) =>
{
    Console.WriteLine("Hey! " + DateTime.Now.ToLongTimeString());
};
timer.Start();
Console.ReadKey();

Existían algunas fórmulas más específicas para plataformas concretas, como las clases System.Windows.Forms.Timer, System.Web.UI.Timer u otras. Sin embargo, todas coincidían en varias cosas:

  • Utilizaban callbacks de alguna u otra forma, lo que implica un cierto riesgo de leaks de memoria y problemas con los tiempos de vida de objetos cuando la cosa se complica.
  • Los callbacks no permitían código asíncrono, lo que podía llevarnos a luchar contra los engorrosos escenarios de ejecución de código asíncrono en entornos síncronos (async-over-sync).
  • Podían darse casos de superposición u overlapping entre las distintas ejecuciones, cuando éstas tardaban en completarse más que el intervalo de definido en el timer.
Compartir:
martes, 18 de enero de 2022
Compartir:
.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.

Compartir:
martes, 23 de noviembre de 2021
Compartir:
.NET

Estamos muy acostumbrados a comenzar nuestros métodos realizando comprobaciones para evitar que pasen a nuestro código valores nulos que pudieran romper la aplicación.

Desde el principio de los tiempos, estas guardas han presentado el siguiente aspecto:

public class MyService
{
    public void MyMethod(object first, object second)
    {
        if(first == null) 
        {
            throw new ArgumentNullException("first");
        }
        if(second == null) 
        {
            throw new ArgumentNullException("second");
        }        
        // ...
    }
}    

¿Todo bien, verdad? El código es aparentemente correcto y fácil de comprender, pero... ¡demasiado extenso! Hemos consumido casi diez líneas de código sólo realizando comprobaciones de "fontanería", y ni siquiera hemos empezado a plantear la funcionalidad real del método.

Afortunadamente, con el tiempo C# ha ido evolucionando y mejorando sucesivamente este escenario tan frecuente.

Compartir:
martes, 13 de abril de 2021
Compartir:
.NET Core

A raíz de los posts sobre generadores de código (como éste y éste), un amigo del blog me escribió para ver si de alguna forma era posible examinar el código fuente generado para poder depurarlo con mayor facilidad.

Y en efecto, es posible. Pero en vez de responderle directamente, he pensado que sería mejor compartirlo por aquí, de forma que pueda resultar de utilidad para alguien más :)

Compartir:
martes, 16 de marzo de 2021
Compartir:
.NET Core

Como decíamos hace unos días, los generadores de código C# nos brindan la posibilidad de crear al vuelo código C# e incluirlo en nuestros proyectos en tiempo de compilación.

Por no alargar demasiado el post, vimos un sencillísimo ejemplo de implementación, pero ahora vamos a crear algo más complejo que podría ayudarnos a solucionar un problema que tendría difícil solución de no contar con esta característica del compilador.

1. Definición de objetivos

El reto al que vamos a enfrentarnos ya lo expusimos en el post anterior como un caso de uso simple de los generadores de código, así que vamos a reproducir la descripción del escenario.

Imaginemos que en nuestra aplicación tenemos clases que representan operadores matemáticos como SumOperator, MultiplyOperator, DivideOperator, SubtractOperator. Imaginad también que nos interesa tener un tipo enum Operators donde aparezca un miembro por cada operador disponible, algo como:

public enum Operators
{
    Sum,
    Multiply,
    Divide,
    Subtract
}

El problema que tiene enfocar esto de forma manual es que resultaría sencillo implementar una nueva clase operador y olvidar crear su correspondiente entrada en la enumeración Operators. Aquí es donde vienen al rescate los generadores de código :)

Lo que implementaremos hoy es un generador de código C# que creará la enumeración por nosotros en tiempo de compilación, manteniéndola sincronizada en todo momento con las clases que tengamos definidas en el proyecto. Para ello, crearemos un generador llamado OperatorsEnumGenerator que:

  • En la fase de análisis de código recopilará las clases del proyecto a compilar cuyo nombre finalice por Operator.
  • En la fase de generación de código creará el enum con los miembros registrados anteriormente.

¡Vamos allá!

Compartir:
martes, 9 de marzo de 2021
Compartir:
.NET Core

Seguramente muchos coincidiremos en que una de las novedades más interesantes de la última versión del compilador de C# es lo que oficialmente han denominado C# Source Generators, o generadores de código fuente de C#.

Muy resumidamente, esta característica añade un nuevo paso en la compilación en el cual los desarrolladores podemos introducir componentes propios (generadores) que inspeccionen el código de la aplicación que está siendo compilada y generen nuevos archivos, que a su vez pueden ser compilados e incluidos en los ensamblados resultantes. Su objetivo, tal y como se declara en su documento de diseño, es posibilitar la metaprogramación en tiempo de compilación.

Veámoslo con un ejemplo donde, además de explicarlo mejor, se puede mostrar su utilidad. Imaginad que en nuestra aplicación tenemos clases que representan operadores matemáticos como SumOperator, MultiplyOperator, DivideOperator, SubtractOperator, y todos ellos heredan de una clase base Operator. Imaginad también que nos interesa tener un tipo enumerado enum Operators donde aparezca un miembro por cada operador disponible, algo como:

public enum Operators
{
    Sum,
    Multiply,
    Divide,
    Subtract
}

Muy probablemente os habéis encontrado alguna vez con un escenario similar y habéis sufrido la dificultad de mantener sincronizada la enumeración con las clases que heredan de Operator: cada vez que aparezca un operador nuevo e implementemos la clase operador que lo representa, tendremos que acordarnos de ir a Operators y añadir el miembro.

Pues bien, aunque simple, esto sería un caso de uso bastante claro para los generadores de código fuente de C#. Gracias a ellos, podríamos crear un componente generador que examine nuestro código en busca de herederos de Operator y genere al vuelo, siempre en tiempo de compilación, un archivo de código con la enumeración Operators.

A todos los efectos, es como si esa enumeración la hubiéramos escrito a mano, porque podremos usarla con normalidad, aparecerá en intellisense, etc., pero la diferencia es que será generada cada vez que compilemos el proyecto, asegurando así que siempre será correcta y completa.

Compartir:
martes, 23 de febrero de 2021
Compartir:
.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?

Compartir:
martes, 10 de marzo de 2020
Compartir:
.NET Core Alguna vez he escuchado, a modo de leyenda urbana, que no era bueno utilizar try/catch porque el mero hecho de usar este tipo de bloque de código afectaba al rendimiento.

La verdad es que de forma intuitiva se puede adivinar que esto no debería ser así. Si no se producen excepciones en un código que tengamos envuelto en un try/catch se debería ejecutar virtualmente a la misma velocidad que si no usáramos este tipo de bloque. Cosa distinta es si producen excepciones: ahí el rendimiento sí se verá seriamente penalizado, pero no es culpa del try/catch sino del propio sistema de gestión de excepciones de .NET.

Pero claro, lo dicho anteriormente es sólo cierto si somos capaces de demostrarlo, así que usaremos nuestro viejo conocido BenchmarkDotnet para desmontar este mito.
Compartir:
martes, 28 de noviembre de 2017
Compartir:
Ya tenemos ganador del sorteo, muchas gracias a todos por participar. ¡Enhorabuena @jordimoz!

Podríamos decir que NDepend es un clásico en el mundo de las herramientas que nos ayudan a mejorar la calidad de nuestro software. Creado por Patrick Smacchia en 2004, pasó a ser comercial en 2007, y desde entonces ha ido mejorando con el objetivo de permitirnos analizar nuestros proyectos desde una perspectiva que difícilmente podríamos conseguir con otras herramientas.

Ya le echamos un vistazo por aquí hace muchos años, y tenía interés por ver cómo había evolucionado el producto y cómo se había adaptado a los cambios en plataformas y tecnologías que se han producido desde entonces, para lo que Patrick me ha brindado amablemente una licencia del producto.

Pero no contento con eso, y sabiendo que tenemos afición a regalar productos para desarrolladores de vez en cuando, también ha cedido una licencia de desarrollador, valorada en 399 Euros para sortear entre los lectores del blog :)
Nota: es importante aclarar que lo que vais a leer a continuación no ha sido filtrado ni condicionado en ningún momento por el equipo de NDepend.
Compartir:
martes, 5 de marzo de 2013
Compartir:
Anónimo
Venga, lo confieso: yo también he generado desde mis aplicaciones contenidos HTML y los he enviado al cliente en un archivo con extensión XLS, incluso modificando el content-type, para que pareciera un documento de hoja de cálculo. Durante años. Y también le he dicho a mis clientes que el molesto mensaje que aparece al abrirlo desde Excel, el que indica que el contenido del archivo no coincide con la extensión del mismo, es algo normal.

Pero esto se acabó desde que descubrí ClosedXML, un magnífico componente para .NET basado en el estándar OpenXML que permite la generación de archivos Excel “de verdad”, con formato, estilos, fórmulas, rangos, filtros, y casi todo lo que se nos pueda ocurrir.
Compartir:
martes, 19 de junio de 2012
Compartir:
ASP.NET MVCUn post rapidito. Según puede consultarse en MSDN, ya tenemos confirmado que la versión 4.5 de .NET framework vendrá acompañada de un nuevo conjunto de atributos de validación para aplicar a las propiedades del Modelo en el espacio de nombres System.ComponentModel.DataAnnotations:
  • CreditCardAttribute, que puede ser utilizado para validar números de tarjeta de crédito.
  • EmailAddressAttribute, que validará direcciones de correo electrónico.
  • FileExtensionsAttribute, para validar extensiones en nombres de archivo.
  • PhoneAttribute, que indica cuándo una propiedad debe contener un número de teléfono válido.
  • UrlAttribute, que comprobará si el contenido de una propiedad es una URL válida.
  • CompareAttribute, que antes estaba disponible en System.Web.Mvc y ha “ascendido” a anotación de datos general, permite validar la igualdad de dos propiedades.
Pues bien, según se observa en este changeset del código fuente del framework, ya podemos asegurar que la versión final de MVC 4 incorporará adaptadores de cliente y servidor para que podamos utilizarlos de forma directa en nuestros desarrollos :-)

También se ha incluido soporte para el atributo MembershipPasswordAttribute, recientemente incluido en System.Web.Security, que comprueba si una contraseña cumple los requisitos establecidos para las mismas en el membership provider.

Publicado en Variable not found.
Compartir:
miércoles, 1 de febrero de 2012
Compartir:
Microsoft .NET Habitualmente asociamos la validación de entidades basadas en anotaciones de datos, o data annotations, a tecnologías como dynamic data o ASP.NET MVC, y estamos acostumbrados a que la validación se realice de forma automática, pero nada más lejos de la realidad. Podemos utilizar data annotations desde cualquier tipo de aplicación .NET (Webforms, Winforms, WPF, Consola, o cualquier otra en la que tengamos disponible System.ComponentModel.DataAnnotations), puesto que existe la posibilidad de invocar manualmente los procedimientos de validación.

En este post vamos a ver cómo realizar validaciones basadas en anotaciones de forma manual, lo cual puede tener su utilidad en gran número de escenarios.
Compartir:
martes, 27 de diciembre de 2011
Compartir:
Enlaces interesantes: .NET, ASP.NET, Azure, HTML, CSS, javascript, Visual StudioEstos son los enlaces publicados en Variable not found en Facebook y Twitter del 18 al 23 de diciembre de 2011. Espero que os resulten interesantes. :-)

.Net

Asp.net

Azure / Cloud

Data access

Html/Css/Javascript

Visual Studio/Complementos

Y no olvidéis que podéis seguir esta información en vivo y en directo desde Variable not found en Facebook, o a través de Twitter.

Publicado en Variable not found
Compartir: