Seguimos analizando las novedades de .NET 9, y en esta ocasión le toca el turno a la actualización en miembros de clases parciales de C# 13.
Las clases o tipos parciales de C# están con nosotros casi desde los inicios de .NET, desde los tiempos de C# 2.0. Como sabemos, éstas permiten dividir la implementación de un tipo en varios archivos. Su existencia se debía a varios motivos:
- Permitir a los desarrolladores trabajar en clases de forma concurrente sin bloquearse (recordemos que por aquellos tiempos aún pululaban por ahí infames sistemas de control de código fuente como SourceSafe).
- Trocear clases extensas para facilitar su comprensión y mantenimiento.
- Y, el que es más importante, posibilitar el uso sencillo de herramientas de generación de código que, de alguna forma, tuvieran que completar la implementación de código del usuario.
Acompañando a las clases parciales, se introdujo también el soporte para métodos parciales. Aunque su uso era limitado y podía depararnos algunas sorpresas, eran útiles para comunicar de forma segura distintos fragmentos de la clase. Básicamente, consistía en que una porción de una clase podía definir un método e invocarlo desde su código, delegando a otras porciones su implementación. Si no se implementaba en ninguna otra parte, simplemente no se generaba el código de la invocación.
Veamos un ejemplo del uso de estas características:
// Archivo: Ejemplo.Main.cs
public partial class Ejemplo
{
// El método parcial se declara sin implementación...
partial void Log(string msg);
public void RealizarAlgo()
{
hacerAlgoComplejo();
Log("¡Ojo!"); // Usamos el método
// parcial declarado antes
}
}
// Archivo: Ejemplo.Log.cs
public partial class Ejemplo
{
partial void Log(string msg)
{
Console.WriteLine(msg);
}
}
Pues bien, casi dos décadas más tarde, los tipos parciales van a recibir algunas actualizaciones interesantes en C# 13, posibilitando que, además de métodos, puedan definirse propiedades parciales.
Aquí tenéis los enlaces recopilados durante la semana.
En esta ocasión, creo que como mínimo vale la pena echar un vistazo a lo nuevo de Anthropic, que sorprende especialmente con computer use, una vuelta de tuerca más para la automatización de tareas con nuestro ordenador. Es increíble que hace sólo unos meses, todas estas novedades que van apareciendo sólo podrían haber cabido en la ciencia ficción...
También muy interesante el movimiento de JetBrains, facilitando el uso gratuito de sus IDEs WebStorm y Rider para proyectos no comerciales 🙂
¡Que los disfrutéis!
Por si te lo perdiste...
- Eliminar el encabezado "X-Powered-By" en ASP.NET Core
José M. Aguilar - Cómo enviar mensajes a la consola del navegador desde Blazor
José M. Aguilar
.NET Core / .NET
- .NET 9 Feature Switches: Trimming the Fat (Like Your Code, Not You)
Cheyenne Sokkappa - Sending Messages to the Original Sender with Wolverine
Jeremy D. Miller - Adding more extensions to Result<T>
Andrew Lock - What are the SOLID Principles in C#? Explained With Code Examples
Danny Adams - Upgrade Your Windows Forms .NET Projects to the Latest .NET Version for Enhanced Security
Shyam Gupta - 10 Most Frequently Asked Questions About JWT in .NET & Implementing JWT Authentication in ASP.NET Core – ArgosCo .net Development Blog
Hatim Rih - A Brief Introduction to the .NET Muxer (aka dotnet.exe)
Steve Gordon
Una de las novedades más destacables de .NET 9 es, sin duda, el nuevo sistema de caché híbrida (Hybrid cache), una puesta al día del sistema de caché distribuida que nos acompaña desde las primeras versiones de .NET Core, y al que iban haciendo falta ya algunas mejoras.
Este nuevo sistema está construido encima de la infraestructura de caching existente (Microsoft.Extensions.Caching
), añadiendo mejoras que hacen que su uso sea más sencillo y contemplando de serie funcionalidades que antes nos veíamos obligados a implementar manualmente.
Vamos a echarle un vistazo a sus principales características :)
Disclaimer: lo que vamos a ver a continuación está basado en .NET 9 RC2, por lo que todavía es posible que haya cambios en la versión final.
Como todos los principios de semana, aquí va la lista de enlaces recopilados durante los últimos siete días. Hay bastantes cosas que me han llamado la atención en esta ocasión; entre otros, he descubierto que Visual Studio incluye un visualizador de JWT integrado, el atributo [OverloadResolutionPriority]
de .NET 9/C#13, y también me he preguntado cuántos son muchos índices en una tabla SQL 😕
Por si te lo perdiste...
- Añadir o eliminar encabezados de respuesta en ASP.NET Core y Core MVC
José M. Aguilar - Obtener el valor de parámetros de la query string con JavaScript
José M. Aguilar
.NET Core / .NET
- Runnable examples
Sergio León - Trace sampling in SerilogTracing
Nicholas Blumhardt - What's new in System.Text.Json in .NET 9
Eirik Tsarpalis - Safety and simplicity with LINQ
Andrew Lock - C# Tip: IEnumerable vs ICollection, and why it matters
Davide Bellone - Understanding OverloadResolutionPriority attribute in C# 13
Gérald Barré - What's new in .NET 9: Cryptography improvements
Ali Hamza Ansari - Automatic Upgrade of .NET and External Dependency Versions, Converting Assembly to Package References and More
Dennis Garavsky - .NET 9.0 LINQ Performance Improvements
NDepend Team - Extending MediatR with publishing strategies
Fati Iseni
Cada vez que llega una nueva versión del framework, todos estamos ansiosos por conocer las novedades, especialmente las más espectaculares. Sin embargo, a veces hay pequeñas mejoras que, sin ser revolucionarias, nos pueden resultar útiles en algunos escenarios.
El modificador params
permite especificar que un método o función acepta un número variable de argumentos, como en el siguiente ejemplo, donde vemos una función Sum()
que puede ser invocada con cualquier número de valores:
// Declaración de la función:
int Sum(params int[] values)
{
return values.Sum();
}
// Ejemplos de uso:
Console.WriteLine(Sum(1)); // Enviamos un único valor -> "1"
Console.WriteLine(Sum(1, 2, 3, 4, 5)); // Enviamos cinco valores -> "15"
Console.WriteLine(Sum()); // No enviamos elementos, se recibirá un array vacío -> "0"
Console.WriteLine(Sum([1, 2, 3])); // Aquí pasamos un array directamente -> "6"
Hasta ahora, el parámetro param
sólo podía ser un array, lo que limitaba un poco su uso. Por ejemplo, no podríamos llamar a la función Sum()
pasándole directamente un List<int>
o un IEnumerable<int>
sin convertirlos antes a array porque fallarían en tiempo de compilación, al no tratarse de un int[]
, que es el tipo del parámetro esperado.
¿Qué ha cambiado?
Aquí tenemos la recopilación de enlaces de la semana. Entre ellos, me han llamado especialmente la atención los datos que muestra Trevor I. Lasn en su artículo sobre el factor edad en el mundo tecnológico, probablemente porque estoy en el 15% del que habla, los desarrolladores que vuelan ya por encima de los 45 tacos 😆
Y aparte, como siempre, un poco de ASP.NET Core, datos, IA, programación web y otros temas que espero que os resulten interesantes 🙂
Por si te lo perdiste...
- Custom middlewares en ASP.NET Core
José M. Aguilar - ¡No uses Replace() para eliminar los guiones de un GUID en .NET!
José M. Aguilar
.NET Core / .NET
- .NET 9 Release Candidate 2 is now available!
.NET Team - Embedding Python into your .NET project with CSnakes
Tony Baloney - Centralize your packages in .NET with NuGet
Steven Giesel - Unleashing .NET 9: Key Performance Boosts and Pitfalls Compared to .NET 8
David McCarter - .NET 8 vs .NET 9: What's the Difference and Should you Migrate?
Cheyenne Sokkappa - Replacing Exceptions-as-flow-control with the result pattern
Andrew Lock - How To Simplify Assertions in Unit and Integration Tests with Verify in .NET
Anton Martyniuk - Domain Events with .NET
Visit profile - Avoiding Repetitive Code With Metalama
Gael Fraiteur - Using Windows Error Reporting in .NET
Kevin Gosse - Have you ever memory-profiled in a unit test?
Dennis Frühauff - Engineering the Scalable Vector Extension in .NET
Kunal Pathak - Typed chat completions with OpenAI and .NET 9
Daniel Cazzulino - Cancellation, Part 6: Linking
Stephen Cleary - Advanced Unit Testing in .NET with xUnit, NSubstitute, and AutoFixture: A Deep Dive into my HangfireDemoApi
Juan Luis Guerrero
Probablemente, todos hemos usado en algún momento el método Guid.NewGuid()
de .NET para generar identificadores únicos globales. Y muchos hemos sufrido con el problema que supone su (pseudo) aleatoriedad, principalmente cuando necesitamos ordenarlos... sobre todo si su destino final es acabar siendo el campo clave de una base de datos relacional.
Pero pocos éramos conscientes de que el método Guid.NewGuid()
de .NET retorna un GUID versión 4, y que existen otras versiones de UUID (¡hasta 8!) que pueden resultar más beneficiosas en determinados escenarios.
Una semana más, aquí tenéis los enlaces recopilados durante la semana pasada, con mucho protagonismo de los últimos lanzamientos de OpenAI que, si usáis sus APIs o planeáis hacerlo, seguro que os interesarán: la API realtime y el prompt caching.
Por si te lo perdiste...
- El filtro [OutputCache], por fin de vuelta en ASP.NET Core
José M. Aguilar - Gestionar errores 404 (y otros) en ASP.NET Core y MVC
José M. Aguilar
.NET Core / .NET
- What's New in .NET 9: A Developer's Perspective
Vinoth Kumar Sundara Moorthy - How To Test Integrations with APIs Using WireMock in .NET
Anton Martyniuk - How to use IHttpClientFactory and WireMock.NET together using Moq
Davide Bellone - Getting the Running Operating System in C#
Bryan Hogan - Serilog fallback sinks
Nicholas Blumhardt - An Efficient Dictionary for IPAddress Tracking using .NET 9 with AlternateLookup and IAlternateEqualityComparer
Steve Gordon - Effortlessly Upload Files and Data in .NET applications using HttpClient
Hariom Dubey - .NET's ActivityListener sampling API
Nicholas Blumhardt - Visualizing the Serilog 4.1 batch retry algorithm
Nicholas Blumhardt
Como sabemos, una forma habitual de registrar servicios en el contenedor de dependencias de .NET consiste en indicar al framework la implementación de la interfaz o clase abstracta que debe ser utilizada cuando se solicite una instancia de la misma. Por ejemplo, en el siguiente código, que podría pertenecer a la inicialización de una aplicación ASP.NET Core, se registra la implementación FriendDbRepository
para la interfaz IFriendRepository
con un ámbito scoped o por petición:
builder.Services.AddScoped<IFriendRepository, FriendDbRepository>();
Hecho esto, podríamos solicitar una instancia de IFriendRepository
en cualquier parte de nuestra aplicación que admita inyección de dependencias, como los controladores, manejadores Minimal API, otras dependencias, etc. El inyector encargará de proporcionarnos una instancia de FriendDbRepository
allá donde la necesitemos:
public class FriendsController: Controller
{
public FriendsController(IFriendRepository friendRepository)
{
// ... Aquí la tenemos!
}
}
O bien, podríamos obtenerla directamente desde la colección de servicios:
public void DoSomething(IServiceProvider services)
{
var repo = services.GetRequiredService<IFriendRepository>();
...
}
Como vemos en ambos casos, la única forma disponible para identificar el servicio que deseamos obtener es utilizar su tipo, o muy habitualmente, la abstracción (interfaz o clase abstracta) a la que está asociado.
Pero eso cambió con la llegada de .NET 8...
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: .net, .net8, aspnetcore, novedades, trucos
Una semana más, comparto la recopilación de enlaces a contenidos que me han parecido interesantes.
De esta tirada, me quedo con "Las interfaces describen qué, las implementaciones describen cómo", un gran título para el artículo de Steve Smith sobre conceptos que todos deberíamos tener claros.
Por si te lo perdiste...
- Cómo crear componentes Blazor usando directamente C# en lugar de la sintaxis Razor
José M. Aguilar - Obtener la última excepción producida en ASP.NET Core
José M. Aguilar
.NET Core / .NET
- (Non-)Nullable Reference Types in C#
Paulo Zemek - Conditional compilation symbols in C#
Bart Wullems - FSZipper in C#
Mark Seemann - Preparing for .NET 9
Mattias Karlsson - A Better Way to Handle Entity Identification in .NET with Strongly Typed IDs
Anton Martyniuk - Boosting Loop Performance in .NET: The Simple Trick of Caching Array Length
David McCarter - Mastering C# 12: New Features and How to Use Them
Cosmic Meta - .NET Terminology
Rockford Lhotka - Interact with Ollama through C#
Bart Wullems - Service Discovery in .NET
Ricardo Peres - Tired of other developers?
Stanislav Nedelchev - Implementing Blocked Floyd-Warshall algorithm for solving all-pairs shortest path problem in C#
Oleg Karasik
Acercándose el lanzamiento de .NET 9 el próximo mes de noviembre, podemos ver ya algunas de las novedades que traerá la nueva versión, con las que podemos jugar desde hace algunos meses instalando las previews que van apareciendo cada mes.
En esta ocasión vamos a ver algunas novedades más interesantes que se han añadido a la biblioteca de serialización JSON oficial, conocida por todos como System.Text.Json
:
- Opciones de personalización de la indentación
- Singleton de opciones de serialización y deserialización para la web
- Soporte de anotaciones de anulabilidad
- Comprobación de parámetros obligatorios del constructor
- Descripción de tipos .NET usando JSON Schema
¡Vamos a ello!
Nota: Este artículo está basado en .NET 9 RC1, por lo que algunas de las características descritas podrían cambiar en la versión final.
1. Opciones de personalización de la indentación
Hasta .NET 8, las opciones de personalización relativas al indentado de JSON generado era bastante limitada. De hecho, sólo podíamos decidir si lo queríamos indentar o no usando la propiedad WriteIndented
de JsonOptions
:
var obj = new
{
Name = "John",
Age = 30,
Address = new { Street = "123 Main St", City = "New York" }
};
var jsonOptions = new JsonSerializerOptions()
{
WriteIndented = true, // Queremos indentar el JSON
};
Console.WriteLine(JsonSerializer.Serialize(obj, jsonOptions));
El resultado será siempre un JSON indentado a dos espacios como el siguiente:
{
"Name": "John",
"Age": 30,
"Address": {
"Street": "123 Main St",
"City": "New York"
}
}
En .NET 9 disponemos de dos nuevas propiedades para personalizar la indentación del JSON generado. La propiedad IndentCharacter
permite especificar el carácter a utilizar (espacio o tabulador, exclusivamente), y mediante IndentSize
podemos indicar el número de caracteres a utilizar en cada nivel de indentación.
var jsonOptions = new JsonSerializerOptions()
{
WriteIndented = true,
IndentCharacter = '\t',
IndentSize = 2
};
Console.WriteLine(JsonSerializer.Serialize(obj, jsonOptions));
En este caso, el resultado será así (2 tabuladores por nivel de indentación):
{
"Name": "John",
"Age": 30,
"Address": {
"Street": "123 Main St",
"City": "New York"
}
}
2. Singleton de opciones de serialización y deserialización para la web
ASP.NET Core utiliza una configuración por defecto para serializar y deserializar objetos JSON, que ahora está disponible de forma pública en la propiedad JsonSerializerOptions.Web
.
El objeto retornado es un singleton JsonSerializerOptions
de sólo lectura, que estará disponible en cualquier punto de la aplicación.
Por defecto, este objeto está configurado de la siguiente manera:
PropertyNameCaseInsensitive
está establecido atrue
, por lo que la deserialización de propiedades es insensible a mayúsculas y minúsculas.JsonPropertyNamingPolicy
tiene el valorJsonNamingPolicy.CamelCase
, así que las propiedades se serializan en formato camelCase, el habitual en JavaScript y JSON.NumberHandling
esJsonNumberHandling.AllowReadingFromString
, así que será posible deserializar números que vengan especificados como texto.
Un ejemplo de utilización de este objeto sería el siguiente:
var obj = new
{
Name = "John",
Age = 30,
Address = new { Street = "123 Main St", City = "New York" }
};
Console.WriteLine(JsonSerializer.Serialize(obj, JsonSerializerOptions.Web));
// Resultado:
// {"name":"John","age":30,"address":{"street":"123 Main St","city":"New York"}}
Dado que el objeto es de sólo lectura, no podemos modificar sus propiedades. Si necesitásemos algún tipo de cambio en la configuración, podríamos crear una configuración nueva partiendo de él, e introducir las modificaciones deseadas, por ejemplo:
var jsonOptions = new JsonSerializerOptions(JsonSerializerOptions.Web)
{
WriteIndented = true,
IndentCharacter = '\t',
IndentSize = 2
};
3. Soporte de anotaciones de anulabilidad
En .NET 8 y versiones anteriores, System.Text.Json
no respetaba las anotaciones de anulabilidad de C# en las propiedades de los tipos de referencia. Por ejemplo, si tenemos una propiedad de tipo referencia que no puede ser nula como en el siguiente caso, podemos ver que el siguiente código deserializará sobre ella un valor nulo sin problema:
var myFriend = JsonSerializer.Deserialize<Friend>("""{ "Name": null }""");
Console.WriteLine(myFriend.Name ?? "Unknown"); // Muestra "Unknown"
// Friend tiene un campo "Name" que no puede ser nulo
public record Friend (string Name);
A partir de .NET 9, podemos indicar opcionalmente a System.Text.Json
que respete la anulabilidad de propiedades, es decir, si una propiedad no es anulable, al deserializarla no podremos establecerla al valor nulo.
Esto podemos conseguirlo simplemente estableciendo a true
la propiedad RespectNullableAnnotations
de las opciones de deserialización:
var opt = new JsonSerializerOptions() { RespectNullableAnnotations = true };
var myFriend = JsonSerializer.Deserialize<Friend>("""{ "Name": null }""", opt);
Console.WriteLine(myFriend.Name ?? "Unknown"); // Lanza una excepción
Según la documentación, otra posibilidad es hacerlo de forma global para todo el proyecto, añadiendo la siguiente configuración en el archivo .csproj
:
<ItemGroup>
<RuntimeHostConfigurationOption
Include="System.Text.Json.JsonSerializerOptions.RespectNullableAnnotations"
Value="true" />
</ItemGroup>
Supongo que, debido a que aún estamos jugando con versiones preview del compilador, esta última opción no conseguí que me funcionara.
Otra cosa que he visto curiosa es que el siguiente código, a mi entender, debería lanzar una excepción, pero no lo hace:
var opt = new JsonSerializerOptions() { RespectNullableAnnotations = true };
var myFriend = JsonSerializer.Deserialize<Friend>("{ }", opt);
Console.WriteLine(myFriend.Name ?? "Unknown"); // Muestra "Unknown"
En principio, si la propiedad Name
hemos dicho que no era anulable, ¿no debería lanzar una excepción al deserializar un objeto que no especifica su valor? Pues no, y parece ser que se trata de un comportamiento esperado; hay un interesante hilo al respecto en GitHub discutiendo el por qué de esta decisión, aunque en el siguiente apartado veremos que existe en algunos casos existe una forma alternativa de evitar esta situación.
Por último, la propiedad RespectNullableAnnotations
también afecta a la serialización, de forma que si una propiedad no anulable tiene un valor nulo al ser serializada, se lanzará una excepción:
var opt = new JsonSerializerOptions() { RespectNullableAnnotations = true };
var myFriend = new Friend(null);
Console.WriteLine(JsonSerializer.Serialize(myFriend, opt)); // Lanza una excepción
4. Comprobación de parámetros obligatorios del constructor
En .NET 8 y anteriores, los parámetros del constructor del tipo a deserializar eran tratados como siempre como opcionales. Por esta razón, el código siguiente no provoca errores:
var myFriend = JsonSerializer.Deserialize<Friend>("""{ }""");
Console.WriteLine(myFriend.Name ?? "Unknown"); // Muestra "Unknown"
public record Friend(string Name);
En .NET 9, es posible indicar al deserializador que queremos respetar los parámetros requeridos del constructor, de forma que si no pueden ser satisfechos se lanzará una excepción. Esto se consigue con el nuevo flag RespectRequiredConstructorParameters
en las opciones de deserialización:
var opt = new JsonSerializerOptions() { RespectRequiredConstructorParameters = true };
var myFriend = JsonSerializer.Deserialize<Friend>("""{ }""", opt);
Console.WriteLine(myFriend.Name ?? "Unknown"); // Lanza una excepción
La excepción, de tipo JsonException
es bastante clara en su texto de descripción: 'JSON deserialization for type 'Friend' was missing required properties including: 'Name'.'.
Como en el caso anterior, este comportamiento puede ser también configurado de forma global para todo el proyecto, añadiendo la siguiente configuración en el archivo .csproj
:
<ItemGroup>
<RuntimeHostConfigurationOption
Include="System.Text.Json.JsonSerializerOptions.RespectRequiredConstructorParameters"
Value="true" />
</ItemGroup>
Y como en el caso anterior, tampoco he conseguido que me funcione de esta forma 😆 Esperemos que las siguientes preview o la versión final lo solucionen.
5. Descripción de tipos .NET usando JSON Schema
La nueva clase estática JsonSchemaExporter
expone el método GetJsonSchemaAsNode()
, que permite obtener el esquema JSON de un tipo .NET en forma de objeto JsonNode
.
Su uso es muy sencillo, simplemente pasamos las opciones a utilizar para serializar el esquema, y el tipo del que queremos obtenerlo. Observad el siguiente ejemplo:
var schema = JsonSchemaExporter.GetJsonSchemaAsNode(
JsonSerializerOptions.Default,
typeof(Friend)
);
Console.WriteLine(schema);
public record Friend(string Name, string? nickName, int Age = 20);
El resultado que obtendremos es una descripción del tipo, siguiendo el estándar JSON Schema, que en este caso sería algo así:
{
"type": [
"object",
"null"
],
"properties": {
"Name": {
"type": "string"
},
"nickName": {
"type": [
"string",
"null"
]
},
"Age": {
"type": "integer",
"default": 20
}
},
"required": [
"Name",
"nickName"
]
}
Conclusión
En este artículo hemos visto algunas de las novedades que traerá la nueva versión de .NET 9 en la biblioteca de serialización JSON oficial, System.Text.Json
. Aunque no son cambios revolucionarios, sí que son mejoras que facilitarán la vida a los desarrolladores que trabajamos con JSON en nuestras aplicaciones.
¡Espero que os haya resultado interesante!
Vamos con la recopilación de enlaces de la tercera semana de septiembre, donde, como es habitual, podemos ver un poco de todo: algunos lanzamientos, características de C#, uso de herramientas de telemetría y monitorización, rendimiento en ASP.NET Core, frontend, y más.
Como reflexión, es curioso ver que la IA está presente en cada vez más áreas del desarrollo. Y aparte, me quedo con lo poco que se suelen usar los operadores implícitos de C#, cuando realmente pueden hacer nuestro código más limpio...
Por si te lo perdiste...
- Atributos genéricos en C# 11
José M. Aguilar - Cacheo de porciones de vistas en ASP.NET Core MVC
José M. Aguilar
.NET Core / .NET
- Join the .NET Smart Components ecosystem
Daniel Roth - TUnit, new testing framework for .NET 8 and up.
Tom Longhurst - Disabling Recording of an Activity (span) in .NET OpenTelemetry Instrumentation
Steve Gordon - How to run .NET apps natively on Arm64 devices
Mark Downie - Understanding the behavior of the XAML Designer with abstract base classes
Peter Spada - A Modern Way to Create Value Objects to Solve Primitive Obsession in .NET
Anton Martyniuk - Using Polly For Retries
Scott Galloway - Managing TaskCancellationTokens in a central service in ASP.NET
Steven Giesel - C# 11.0 new features: ref fields and the scoped keyword
Ian Griffins - Use the implicit operator to reduce noise in your code
Josef Ottosson
Una pregunta que me hacen con cierta frecuencia los alumnos de mi curso de ASP.NET Core en CampusMVP es que por qué, al ejecutar una aplicación de este tipo, Visual Studio les muestra un mensaje como el siguiente, no se lanza el navegador y no pueden acceder a la aplicación:
Generalmente la respuesta es bastante sencilla: Visual Studio nos está informando de que el servidor web no ha sido lanzado al ejecutar la aplicación.
Tras unas semanas de merecido descanso, volvemos a la carga con el blog, inaugurando lo que, más o menos, debería ser su temporada número 18. Como de costumbre, intentaremos seguir la serie semanal de enlaces de interés y escribiendo sobre trucos, novedades y características de C#, .NET, ASP.NET Core, Blazor, JavaScript, o cualquier cosa que se ponga por delante y que considere que puede ser de utilidad para la comunidad.
Y para empezar con buen pie, lo haremos con la recopilación número 576 de enlaces que, como de costumbre, espero que os resulten interesantes. Especialmente destacable, además del lanzamiento de .NET 9 RC1, es el esperado post "Performance improvements in .NET 9" del gran Stephen Toub, todo un clásico cuando se va acercando una nueva versión del framework. Sencillamente imprescindible si queréis estar a la última.
Por si te lo perdiste...
- Creación de enlaces con tag helpers de Core MVC
José M. Aguilar - Restricciones de ruta personalizadas parametrizables en ASP.NET Core
José M. Aguilar
.NET Core / .NET
- Analizando tu código sin morir en el intento
Adrián Díaz Cervera - .NET 9 Release Candidate 1 is now available!
.NET Team - Performance Improvements in .NET 9
Stephen Toub - The Best Way To Validate Objects in .NET in 2024
Anton Martyniuk - Back to Basics: Await a Task with a Timeout
Rick Strahl - Working with tar files in .NET 8
Andrew Lock - Why is F# code so robust and reliable?
Akunyili Chukwuma - What are partial classes in C# and why do we use them?
David Grace - Optimizing Hash Code Generation in .NET: A Performance Comparison
David McCarter - Thread-Safe Singleton in C#: A Guide to Double-Checked Locking and Lazy<T> Approaches
Antonio Ripa - Intersperse Values for Enumerable Collections
Khalid Abuhakmeh - Alternate Lookup for Dictionary and HashSet in .NET 9
NDepend Team - Autofixture and IOptions<T> - A Winning Combination
Adam Storr
Hago una ligera interrupción de mis vacaciones blogueras para compartir con todos vosotros que, por decimocuarto año consecutivo, he sido reconocido por Microsoft como Most Valuable Professional (MVP) en la categoría .NET ¡¡Hurra!! 😀
Aunque ya debería estar acostumbrado, siempre es un honor y un privilegio recibir este galardón que, de alguna forma, reconoce mis aportaciones a la comunidad de desarrolladores, esa comunidad con la que sigo estando en deuda por lo que he recibido de ella durante tanto tiempo.
Muchas gracias al equipo de Microsoft que hace esto posible, con nuestra querida Cristina González a la cabeza, por la dedicación y esfuerzo que ponen en este programa. Muchas gracias también a todos los compañeros MVPs que hacen que esta comunidad sea tan especial.
Y, por supuesto, agradeceros a todos vosotros, queridos amigos y amigas, por seguir ahí, porque vuestras visitas, lecturas y comentarios son los que me animan a seguir compartiendo experiencias y conocimientos en este pequeño rinconcito de la web.
¡Nos vemos a la vuelta!
Publicado en Variable not found.
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Inyección de dependencias en ASP.NET Core(actualizado)
José M. Aguilar - Añadir al routing de Blazor páginas definidas en otros proyectos o ensamblados
José M. Aguilar
.NET Core / .NET
- Flexible PDF Reporting in .NET Using Razor Views
Milan Jovanović - MemoryCache in C#: A Practical Guide
Metalama Team - Local Functions vs Lambda Expressions in C#
Bozo Spoljaric - Creating hashes in .NET
Damien Bowden - How to find a client's geolocation in .NET with IP lookup
David Grace - Publish a console app as a single executable
Bart Wullems - How to output a SARIF file from a .NET project
Gérald Barré - An immutable priority collection
Mark Seemann - Exploring the generated code: the spread element
Andrew Lock - Readonly, Immutable, and Frozen Collections in .NET
NDepend Team - How to Read appsettings.json in a .NET Console Application
Matheus Dasuke - Scan HTML faster with SIMD instructions: .NET/C# Edition
Daniel Lemire
Cuando en un bucle for
necesitamos más de una variable de control, lo habitual es inicializarlas fuera del bucle y luego usarlas en su interior, por ejemplo, como en el siguiente código:
int j = 0;
for (int i = 0; i < 10 && j < 100; i++)
{
Console.WriteLine($"i = {i}, j = {j}");
j+= 10;
}
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Miembros de inicialización obligatoria en C#11 (required members)
José M. Aguilar - Tag helpers en ASP.NET Core MVC: Introducción
José M. Aguilar
.NET Core / .NET
- Primary constructors
Mark Downie - Proxy Design Pattern in C#
Ahsan Ullah - How to Generate Large Datasets in .NET for Excel With OpenXML
Artem Rudiakov - Exploring the generated code: T[], Span<T>, and Immutable collections
Andrew Lock - When to create a new .csproj?
NDepend Team - ReadOnlySet<T> in .NET 9
Steven Giesel - C# Tip: Path.Combine and Path.Join are similar but way different.
Davide Bellone
Blazor ha venido en ASP.NET Core 8 cargadito de novedades, aunque probablemente la más destacable sea la introducción de las Blazor Web Apps como modelo de proyecto que unifica los distintos modos de renderizado de componentes. Aunque se trata de un cambio positivo, la realidad es que ha complicado algunas cosas que antes, con unos modelos de proyecto más sencillos, eran más fáciles de implementar.
Un ejemplo claro lo tenemos en las páginas de error 404 (not found): con este nuevo modelo unificado no hay una fórmula trivial o integrada de serie en el framework para implementar esta funcionalidad, tan habitual en nuestras aplicaciones.
En este post vamos a ver un posible enfoque para conseguir que si un usuario introduce una ruta inexistente en el navegador o bien pulsa un enlace interno que no exista, podamos mostrarle una página de error 404 totalmente personalizada, implementada como componente Blazor, e integrada en nuestra Blazor Web App.
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- ¡No uses ToLower() o ToUpper() para comparar cadenas en C#!
José M. Aguilar - El archivo _ViewImports en ASP.NET Core MVC
José M. Aguilar
.NET Core / .NET
- Refactor your code with default lambda parameters
David Pine - Cancellation Tokens in C#
Rasheed K Mozaffar - When does the try, catch and finally blocks run in C#?
David Grace - Collection Performance: Introducing FrozenSet in .NET 8: Benefits and Benchmark PerformanceCollection Performance
David McCarter - Privacy and security improvements in .NET modernization tooling
Mike Rousos - How to Cast an Int to an Enum in C#
Chris Pietschmann - Exploring the generated code: List<T> and fallback cases
Andrew Lock - How to Ignore a Property in AutoMapper
Żaneta Borowska - C#: Read Text and JSON File Contents into Variable in Memory
Chris Pietschmann - How to Use FakeLogger to Test Logging Code in .NET
Osman Sokuoglu - How to Use C# to Properly Follow OAuth Authentication Flows
Peter Smulovics - C# 13 params collections
NDepend Team - What Does the null! Statement Do?
Januarius Njoku - Announcing Third Party API and Package Map Support for .NET Upgrade Assistant
Marco Goertz - The Best Way To Map Objects in .Net in 2024
Anton Martyniuk
Llevamos ya varias entregas de la serie C# bizarro, donde, como sabéis, ponemos a prueba nuestros conocimientos de C# llevando al extremo el uso de algunas de sus características, lo que de paso nos ayuda a conocer mejor aún el lenguaje.
Hoy vamos a proponer un nuevo reto, mostrando un comportamiento que quizás muchos conozcáis, pero seguro que otros nunca habéis tenido ocasión de verlo. A ver qué tal se os da ;)
Observad atentamente esta sencilla aplicación de consola:
int i = 1;
int accum = 0;
while (i <= 5)
{
accum += i;
Debug.WriteLine($"Adding {i++}, Accumulated = {accum}");
}
Console.WriteLine($"Total: {accum}");
A la vista del código, las preguntas son dos: ¿Qué resultado obtenemos al ejecutar la aplicación? ¿Será siempre así?
Obviamente, tiene algo de truco, pero si lo pensáis un poco seguro que podéis responder a las preguntas. Y si no, pulsad aquí para ver la solución; 👇👇
En principio, lo lógico es pensar que deberíamos ver en el canal de depuración (la ventana Output>Debug en Visual Studio) el valor actual y el total acumulado en cada iteración, lo que nos permite ir siguiendo en detalle el funcionamiento interno:
Adding 1, Accumulated = 1
Adding 2, Accumulated = 3
Adding 3, Accumulated = 6
Adding 4, Accumulated = 10
Adding 5, Accumulated = 15
Finalmente, se mostrará por consola el valor definitivo:
Total: 15
Esto será justamente lo que obtengamos si copiamos el código en Visual Studio y lo ejecutamos pulsando F5 o el botón correspondiente del IDE. Hasta ahí, perfecto ;)
Sin embargo, este código tiene una trampa oculta. Si desde Visual Studio cambiamos el modo de compilación a "Release" y lo lanzamos, o bien si lanzamos directamente el ejecutable que encontraremos en la carpeta bin/Release/net8.0
(o la versión de .NET que uses, da igual), veremos que la aplicación no se detiene nunca (?!)
El motivo de este extraño comportamiento lo explicamos hace ya bastantes años por aquí, en el post métodos condicionales en C#.
Estos métodos, presentes desde las primeras versiones de .NET (pre-Core), se decoran con el atributo [Conditional]
para hacer que éstos y todas las referencias a los mismos sean eliminadas del ensamblado resultante de la compilación si no existe una constante de compilación determinada.
De hecho, si acudimos al código fuente de la clase estática Debug
, veremos que su método WriteLine()
está definido de la siguiente manera:
public static partial class Debug
{
...
[Conditional("DEBUG")]
public static void WriteLine(string? message)
=> s_provider.WriteLine(message);
}
Cuando compilamos en modo depuración, la constante DEBUG
estará definida, por lo que este método podrá ser invocado con normalidad y todo funcionará bien. Sin embargo, si compilamos en Release o cualquier otra configuración que no incluya la constante, este método desaparecerá del ensamblado, junto con las referencias que lo utilicen.
Es decir, si usamos el modo Release, el código que hemos escrito antes quedará tras la compilación como el siguiente:
int i = 1;
int accum = 0;
while (i <= 5)
{
accum += i;
// La siguiente llamada será eliminada en compilación:
// Debug.WriteLine($"Adding {i++}, Accumulated = {accum}");
}
Console.WriteLine($"Total: {accum}");
Fijaos que, al eliminar la llamada, con ella desaparecerá también la expresión de autoincremento del índice del bucle i++
, por lo que nunca se alcanzará la condición de salida y quedará iterando de forma indefinida.
Bonito, ¿eh? ;)
Publicado en Variable not found.
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- El nuevo modificador "file" de C# 11: tipos visibles exclusivamente en el archivo en el que se definen
José M. Aguilar - La carpeta wwwroot en ASP.NET Core
José M. Aguilar
.NET Core / .NET
- Retrying Failed HttpClient Requests in .NET Using Polly
Gergő Vándor - Does an HttpClient await the Header and the body?
Steven Giesel - Frozen Collections in .NET 8
Sharmila Subbiah - How to Use the LINQ ToDictionary Method in C#
Januarius Njoku - Introducing collection expressions in C#12
Andrew Lock - Differences Between Span and Memory in C#
Bozo Spoljaric - 5 Practical Ways to Add Polly to Your C# Application
Metalama Team - C# Array and List Fastest Loop in 2024
NDepend Team - Weak Events in C#
Satya Prakash - What’s New in C# 13 for Developers?
Vinoth Kumar Sundara Moorthy - C# Version String Formatting
Rick Strahl - Weak Events in C#
Satya Prakash - Creating Custom Attributes in C#
Anton Martyniuk
Este post pertenece a una serie de tres partes donde estamos viendo cómo renderizar componentes Blazor en el interior de vistas MVC de ASP.NET Core. Hasta ahora, hemos visto cómo renderizar desde vistas MVC componentes Blazor usando los siguientes modos de renderización:
En esta entrega final veremos cómo renderizar componentes Blazor ejecutados por completo en el lado cliente (WebAssembly).
Este post pertenece a una serie de tres partes donde estamos viendo cómo renderizar componentes Blazor en vistas MVC de ASP.NET Core.
En la primera parte de la serie vimos cómo renderizar componentes estáticos (SSR) en servidor, y ahora vamos a centrarnos en hacerlo con componentes con interactividad también en el lado servidor (Blazor Server), dejando para una siguiente entrega los componentes interactivos ejecutados por completo en cliente con WebAssembly.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: aspnetcoremvc, blazor, blazorserver, trucos
Hace no demasiado, mientras analizábamos la posibilidad de que Blazor acabara en algún momento sustituyendo a MVC como tecnología "por defecto" para el desarrollo de aplicaciones web en .NET, comentaba que técnicamente no hay nada que impida a ambas tecnologías convivir pacíficamente en una misma aplicación. De hecho, están diseñadas para trabajar juntas :)
En este sentido, uno de los escenarios soportados es la inserción de componentes Blazor en el interior de vistas de una aplicación ASP.NET Core MVC. Esto puede ser muy interesante, entre otros casos, si queremos ir introduciendo Blazor progresivamente en aplicaciones MVC existentes o para reutilizar componentes entre distintos proyectos.
En esta miniserie vamos a ver cómo conseguirlo con los distintos modos de renderizado de Blazor, porque cada uno tiene sus particularidades:
- Renderizado estático (SSR), lo que veremos en este post.
- Renderizado en servidor (Blazor Server), en un futuro post.
- Renderizado en cliente (Blazor WebAssembly), también en un artículo posterior.
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- ¿Cómo se llama el operador "-->" de C#?
José M. Aguilar - ¿Se pueden lanzar dos aplicaciones ASP.NET Core desde el mismo host?
José M. Aguilar
.NET Core / .NET
- Top 10 C# Recent Improvements
NDepend Team - How to Print a 2D Array to the Console in C#
Ellie Zubrowski - How to use C# async/await for API calls & to stop blocking
David Grace - Refactor your code using alias any type
David Pine - MSTest 3.4 is here with WinUI support and new analyzers!
Amaury Levé & Marco Rossignoli - State Machines, Light Switches, and Space Travel with Stateless and .NET 8
Khalid Abuhakmeh - Fast and Compact Structured Logging in C# Using String Interpolation
Metalama team - Automate your .NET SDK updates for consistent and reproducible builds with global.json and Renovate
Anthony Simmon - StringBuilders magic for very large strings
Steven Giesel - .NET Core–Check if console app is running with administrative privileges
Bart Wullems - New And Proposed Changes For C# 13
Peter Ritchie - Bouncy Castle Cryptography Library for .NET
Alvaro Montoya - Async Await Mastery: Handling Exceptions Like a Pro in C#
Kons Fedorov - IDisposable: What Your Mother Never Told You About Resource Deallocation
Stephen Cleary - How To Write Elegant Code with C# Switch Expressions
Anton Martyniuk
El espacio de nombres System.IO
de .NET proporciona clases para trabajar con archivos y directorios, algo que, a priori, no tendría demasiado sentido en componentes Blazor WebAssembly, ya que éstos se ejecutan en el navegador del cliente y, por motivos de seguridad, no tienen acceso al sistema de archivos del servidor, ni tampoco a los archivos del dispositivo del usuario que está ejecutando la aplicación.
El primero de los casos es salvable: si desde un componente Blazor WebAssembly quisiéramos leer o escribir archivos en el servidor, podríamos hacerlo a través de una API que, ejecutada en el servidor, tuviera acceso a este tipo de recursos.
Sin embargo, técnicamente no hay forma de acceder a los archivos locales del usuario, dado que los componentes Blazor WebAssembly se ejecutan en el sandbox del navegador, que impide acceder a los recursos del sistema.
Pues bien, a pesar de ello, y de forma totalmente contraria a lo que podríamos intuir, desde Blazor WebAssembly sí que se pueden usar los componentes de System.IO
(bueno, o muchos de ellos) para leer o escribir archivos y directorios, aunque con algunas peculiaridades que veremos a continuación.
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Personalizar restricciones de ruta en línea en ASP.NET Core
José M. Aguilar - Inyección de dependencias en filtros ASP.NET Core MVC
José M. Aguilar
.NET Core / .NET
- ¿.NET 7 no soportado, pero .NET 6 sí? - Cómo funcionan las versiones en .NET
José Manuel Alarcón - .NET Workloads 101
Miguel Angel Martín Hernández - Introducing links to source code for .NET API Docs
Min Huang - How to test a Roslyn analyzer
Gérald Barré - How to Compare Two Dictionaries in C#
Januarius Njoku - Blocking primary constructor member capture using a Roslyn Analyzer
Andrew Lock - Fastest Ways to Perform Base 10 Conversions in C#
Martin Chege - What's New in C# 13: Enhanced Params, Performance Boosts, and New Extension Types
Almir Vuk - .NET Core Configuration in .NET 8
Dhananjeyan Balaretnaraja - Introduction to the StronglyTypedId Package in .NET
Caleb Okechukwu - Running a Hosted Service in a Console Application
Bryan Hogan - Cancel WhenAny - linked CancellationTokenSource
Steven Giesel - Metadata Consulting [dot] ca: C# dotNet The fastest way to check if a file is a PDF file or not
Metadata Consulting - Lookup in C#
Januarius Njoku - ASP.NET Core Basics: Getting Started with GraphQL
Assis Zang - Using thread-safe classes doesn’t make your code thread-safe
Henrique Dalcin Dalmas - How to Set the Exit Code for a Console Application in .NET
Stefan Cruz
Me da la impresión de que el operador with
de C# no es demasiado conocido, y creo que vale la pena echarle un vistazo porque puede resultarnos útil en nuestro día a día.
Introducido con C# 9, allá por 2020, las expresiones with
proporcionan una forma sencilla de crear nuevas instancias de objetos "copiando" las propiedades de otro objeto, pero con la posibilidad de modificar algunas de ellas sobre la marcha. Esto, aunque puede usarse con varios tipos de objeto, es especialmente interesante cuando estamos trabajando con componentes inmutables, como son los records
, porque la única posibilidad que tenemos de alterarlos es creando copias con las modificaciones que necesitemos.
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Los diez mandamientos del egoless programming
José M. Aguilar - Cadenas de texto con ayudas sintácticas en .NET 7 (y anteriores)
José M. Aguilar
.NET Core / .NET
- .NET Announcements & Updates from Microsoft Build 2024
.NET Team - Announcing NuGet 6.10
The NuGet Team - How to use HttpClient correctly to avoid socket exceptions
David Grace - .NET 7 Support Just Ended, Here's How to Upgrade to .NET 8
David Ramel - MemoryCache, DistributedCache and HybridCache
Steven Giesel - Roslyn analyzers: How to
Gérald Barré - How to Deal With NullReferenceException? Object reference not set to an instance of an object.
Metalama team - Thoughts about primary constructors: 3 pros and 5 cons
Andrew Lock
Revisando código ajeno, me he encontrado con un ejemplo de uso del operador null coalescing assignment de C# que me ha parecido muy elegante y quería compartirlo con vosotros.
Como recordaréis, este operador, introducido en C# 8, es una fórmula muy concisa para asignar un valor a una variable si ésta previamente es null, una mezcla entre el operador de asignación y el null coalescing operator que disfrutamos desde C# 2:
// C# "clásico":
var x = GetSomething();
...
if(x == null) {
x = GetSomethingElse();
}
// Usando null coalescing operator (C# 2)
var x = GetSomething();
...
x = x ?? GetSomethingElse();
// Usando null coalescing assignment (C# 8)
var x = GetSomething();
...
x ??= GetSomethingElse();
Pero, además, al igual que otras asignaciones, este operador retorna el valor asignado, lo que nos permite encadenar asignaciones y realizar operaciones adicionales en la misma línea.
Por ejemplo, observad el siguiente código:
Console.WriteLine(y ??= "Hello, World!");
Aquí, básicamente lo que hacemos es asignar el valor "Hello, World!" a la variable si ésta contiene un nulo, y luego imprimirlo por consola. Y si no es nulo, simplemente se imprime su valor actual.
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Cómo contar y enumerar los items almacenados en un objeto MemoryCache en .NET 6
José M. Aguilar - Interpolación de cadenas en C# 6, a fondo
José M. Aguilar
.NET Core / .NET
- .NET: Eligiendo el framework de Interfaz de Usuario más adecuado para crear aplicaciones de escritorio
José Manuel Alarcón - Read and Write Azure Blob Storage with C#
Chris Pietschmann - What’s new in Orleans 8 for Scalable Distributed Applications
Mike Kistler - C# .NET 8 — MediatR Pipelines
Gabriele Tronchin - How to Find the Latitude and Longitude of a Location in C#
Ellie Zubrowski - An introduction to primary constructors in C#12
Andrew Lock - Mastering Exception Handling in C#: A Comprehensive Guide
Anton Martyniuk - Modeling: Date vs. DateTime
Oren Eini - How to avoid conflicts when testing your dotnet templates locally
Gérald Barré - Scheduled Message Delivery with Wolverine
Jeremy D. Miller - The .NET Generic Math Library
NDepend Team - How to Generate a Random Color Name in C#
Ellie Zubrowski - Collection Performance: Exercise Caution When Using Take() with a Basic Count
David McCarter
Cuando trabajamos con diccionarios en .NET, es muy común comprobar si existe un elemento con una clave determinada antes de obtenerlo para evitar una excepción KeyNotFoundException
:
var numbers = new Dictionary<int, string>()
{
[1] = "One",
[2] = "Two",
[3] = "Three"
};
// Aseguramos que el elemento existe antes de obtenerlo
if (numbers.ContainsKey(3))
{
Console.WriteLine(numbers[3]);
}
Sin embargo, esta comprobación tiene coste. Es decir, aunque el acceso a los diccionarios sea muy rápido -O(1)-, no quiere decir que no consuma tiempo, por lo que es interesante conocer alternativas más eficientes para estos escenarios.
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Cómo modificar la reason phrase en una respuesta HTTP de ASP.NET Core
José M. Aguilar - 101 citas célebres más del mundo de la informática (¡505 ya!)
José M. Aguilar
.NET Core / .NET
- C#: How to Enumerate over Enum values and names
Chris Pietschmann - .NET Core - View outdated packages
Bart Wullems - .NET 8 — Frozen Collections
Henrique Siebert Domareski - 7 Most Common Mistakes in C# Programming
Konstantin Fedorov - How to check if a DLL and an exe is a .NET assembly
Gérald Barré - Serialization and Deserialization with YamlDotNet
Muhammad Afzal Qureshi - C# Discriminated Union: What’s Driving the C# Community’s Inquiries?
NDepend Team - String Performance: Checking for a Character
David McCarter - Refactor your code with C# collection expressions
David Pine - Enforce architectural constrains using NetArchTest
Dennis Frühauff - .NET 9 LINQ Performance Edition
Steven Giesel - General Performance Tip: Type Conversion
David McCarter - Generate QR Codes With QRCoder in .NET
Ryan Miranda - Fast Search and Replace in Large Number of Files: A Practical Guide
Brian C Hart - Support Corner: Dotfuscator and Strong Named Assemblies
Ben Nettleton
Buah, siempre se dice que el tiempo pasa volando, pero es que es verdad. Parece que fue ayer cuando decidí comenzar a escribir en este blog, y hoy hace 18 años de eso. ¡El blog ya es mayor de edad! 🎉
Cuando empecé a escribir por aquí, aún bajo el dominio de Blogger jmaguilar.blogspot.com y con un nombre ligeramente más largo (Variable not found, 0:1, como homenaje a mi querido ZX Spectrum), Windows XP aún llenaba de color las pantallas de nuestros vetustos PC, aunque empezábamos a oír hablar de un nuevo sistema operativo llamado Windows Vista.
Programábamos aplicaciones ASP.NET 2.0 en Visual Studio 2005, usando un C# que apenas acababa de incorporar el soporte para generics o tipos anulables, y las desplegábamos por FPT a hostings o servidores on-premise, porque las nubes eran simplemente algo con formas caprichosas que veíamos en el cielo.
Los teléfonos móviles no eran nada inteligentes, eso de los smartphones llegó más tarde. En aquellos tiempos estos dispositivos sólo servían para hacer llamadas y enviar SMS. A lo sumo, podíamos jugar al Snake, pero poco más. Nada que ver con los dispositivos que llevamos en el bolsillo en este momento, ni las herramientas que éstos han propiciado.
El código fuente de los proyectos lo guardábamos donde podíamos, muchas veces incluso sin usar sistema de gestión de código o control de versiones, o utilizando herramientas poco amigables (¿alguien tiene todavía pesadillas con Source Safe? 😉)
Internet era muy distinto. Aunque las ADSL ya habían llegado a muchos hogares, la velocidad de conexión era mucho menor que la que disfrutamos ahora. Servicios, que ahora son indispensables para nuestro trabajo, como Stack Overflow o GitHub aún no existían, como tampoco (o estaban muy en pañales) las redes y aplicaciones sociales que hoy en día nos mantienen informados y en contacto, como Twitter (creo que nunca me acostumbraré a llamarlo "X" 🤦♂️), Instagram, Facebook, WhatsApp, Youtube, Twitch o TikTok.
Y por supuesto, esto de la inteligencia artificial tal y como la estamos conociendo en este momento era absolutamente impensable. Los que no trabajábamos en ese campo, apenas empezábamos a oír hablar de cosas como el machine learning o redes neuronales, y no tenían aplicación en aplicaciones, digamos, "normales".
Las cosas han cambiado mucho desde entonces, pero una sigue igual: mi pasión por la tecnología y por compartir lo que voy aprendiendo siguen intactos. Por eso, aunque a veces cueste encontrar tiempo para escribir, sigo aquí, en este rincón de la red, compartiendo con vosotros lo que voy aprendiendo, lo que me parece interesante o lo que creo que puede ser útil para alguien.
Pero todos es gracias a vosotros, esa pequeña pero fiel pandilla de amigos que me lee, me comenta y me anima a seguir adelante. ¡Muchas gracias a todos!
Y como todos los años por estas fechas, vamos a hacer balance de cómo han ido las cosas en el blog, que ya os adelanto que regular 😔
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Validación de servicios en entornos distintos a "Development" en ASP.NET Core 6
José M. Aguilar - La nueva directiva @inject de ASP.NET Core MVC
José M. Aguilar
.NET Core / .NET
- Adventures serializing absolutely everything in C#
Isadora Sophia Rodopoulos - Switch expression for void methods
Bart Wullems - Introducing OpenSilver 2.2 with LightSwitch Support
OpenSilver - Creating a .NET AsciiMath parser and using it in a Markdig extension
Andrew Lock - Secure your container build and publish with .NET 8
Richard Lander - Working with Rust Libraries from C# .NET Applications
Khalid Abuhakmeh - Some more C# 12
Tom Deseyn - How does a List know that you changed it while enumerating it?
Steven Giesel - Checking your solution for NuGet vulnerabilities or deprecated packages
Steven Giesel - Generate Images in C# Using ImageMagick & Generate Images in C# Using ImageSharp
Emmanuel Adom - General Performance Tip: Choosing Between Conditional Statements – If, Switch, and Switch Expression Performance in C#
David McCarter