sábado, 29 de noviembre de 2025

LINQ: un lenguaje de consulta para C#

LINQ (Language Integrated Query) es una característica de C# que permite consultar y manipular datos directamente desde el lenguaje, usando una sintaxis coherente y expresiva. En lugar de escribir SQL o recorrer colecciones manualmente, LINQ te deja trabajar con objetos, colecciones, XML o bases de datos de forma declarativa.

Su ventaja primordial es: unificar la forma de consultar datos, sin importar el origen.

Se puede emplear de esta forma:

1. Sintaxis de consulta (query syntax, parecida a SQL):

var resultado = from p in db.Persons
                where p.City == "CDMX"
                orderby p.LastName
                select p;

2. Sintaxis de métodos (method syntax, con lambdas y métodos de extensión):

var resultado = db.Persons
                  .Where(p => p.City == "CDMX")
                  .OrderBy(p => p.LastName)
                  .ToList();

Como se puede observar ambas sintaxis, aunque diferentes, hacen lo mismo.

Usando LINQ en nuestro proyecto

En el post anterior vimos cómo crear una aplicación dotnet que intergra EntityFramework para trabajar con una base de datos de MariaDB. Usemos LINQ como herramienta para hacer SELECT, INSERT, UPDATE y DELETE de forma sencilla.

1. Hacer consultas como un SELECT:

using var db = new AppDbContext();

var personsInCDMX = db.Persons
                      .Where(p => p.City == "CDMX")
                      .OrderBy(p => p.LastName)
                      .ToList();

foreach (var p in personsInCDMX)
{
    Console.WriteLine($"{p.FirstName} {p.LastName} - {p.City}");
}

2. Insertar datos como un INSERT INTO:

db.Persons.Add(new Person {
    PersonId = 2,
    FirstName = "Ana",
    LastName = "García",
    Address = "Av. Reforma 456",
    City = "CDMX"
});
db.SaveChanges();

3. Modificar datos como un UPDATE:

var person = db.Persons.FirstOrDefault(p => p.PersonId == 2);
if (person != null)
{
    person.City = "Guadalajara";
    db.SaveChanges();
}

4. Remover datos como un DELETE:

var person = db.Persons.FirstOrDefault(p => p.PersonId == 2);
if (person != null)
{
    db.Persons.Remove(person);
    db.SaveChanges();
}

El código Program.cs quedaría de la siguiente manera:

using System;
using System.Linq;
using Model;

class Program
{
    static void Main()
    {
        using var db = new AppDbContext();

        // INSERT
        db.Persons.Add(new Person {
            PersonId = 2,
            FirstName = "Ana",
            LastName = "García",
            Address = "Av. Reforma 456",
            City = "CDMX"
            });
        db.SaveChanges();

        // SELECT
        var personsInCDMX = db.Persons
                      .Where(p => p.City == "CDMX")
                      .OrderBy(p => p.LastName)
                      .ToList();
        foreach (var p in personsInCDMX)
        {
            Console.WriteLine($"{p.FirstName} {p.LastName} - {p.City}");
        }
        
        // UPDATE
        var person = db.Persons.FirstOrDefault(p => p.PersonId == 2);
        if (person != null)
        {
            person.City = "Guadalajara";
            db.SaveChanges();
        }

        // DELETE
        if (person != null)
        {
            db.Persons.Remove(person);
            db.SaveChanges();
        }
        
    }
}

Y podríamos hacer más ejemplos.

Filtrar registros (Where):

// Personas que viven en CDMX
var enCDMX = db.Persons
               .Where(p => p.City == "CDMX")
               .ToList();

Seleccionar campos específicos:

// Solo nombres y apellidos
var nombres = db.Persons
                .Select(p => new { p.FirstName, p.LastName })
                .ToList();

Ordenar (ORDER BY):

// Ordenar por apellido
var ordenados = db.Persons
                  .OrderBy(p => p.LastName)
                  .ToList();

Combinar condiciones:

// Personas en CDMX o Guadalajara
var filtrados = db.Persons
                  .Where(p => p.City == "CDMX" || p.City == "Guadalajara")
                  .ToList();

Agrupar (GROUP BY):

// Agrupar personas por ciudad
var agrupados = db.Persons
                  .GroupBy(p => p.City)
                  .Select(g => new { Ciudad = g.Key, Total = g.Count() })
                  .ToList();

Unir tablas (JOIN):

var consulta = db.Persons
                 .Join(db.Orders,
                       p => p.PersonId,
                       o => o.PersonId,
                       (p, o) => new { p.FirstName, p.LastName, o.OrderDate })
                 .ToList();

Paginar (Skip y Take):

// Obtener los primeros 10 registros
var primeros10 = db.Persons
                   .Take(10)
                   .ToList();

// Saltar los primeros 10 y traer los siguientes 5
var pagina2 = db.Persons
                .Skip(10)
                .Take(5)
                .ToList();

Y otras cosas más.

Contar registros (Count):

// Total de personas en la tabla
int total = db.Persons.Count();

// Personas en CDMX
int enCDMX = db.Persons.Count(p => p.City == "CDMX");

Sumar valores (Sum):

// Suma de todas las edades
int sumaEdades = db.Persons.Sum(p => p.Age);

Obtener promedio (Average):

// Edad promedio
double promedioEdad = db.Persons.Average(p => p.Age);

Agrupación con agregados:

// Número de personas por ciudad
var personasPorCiudad = db.Persons
    .GroupBy(p => p.City)
    .Select(g => new {
        Ciudad = g.Key,
        Total = g.Count()
    })
    .ToList();

Búsquedas con Any/All:

// ¿Existe alguien en Monterrey?
bool existeMonterrey = db.Persons.Any(p => p.City == "Monterrey");

// ¿Todos viven en CDMX?
bool todosCDMX = db.Persons.All(p => p.City == "CDMX");

Proyección de cálculos:

// Proyectar nombre completo y longitud del apellido
var consulta = db.Persons
    .Select(p => new {
        NombreCompleto = p.FirstName + " " + p.LastName,
        LargoApellido = p.LastName.Length
    })
    .ToList();

LINQ es una herramienta que nos ayuda a realizar consultas de una manera sencilla.

Enlaces:

https://codemonkeyjunior.blogspot.com/2025/11/entity-framework-un-orm-para-net.html
https://learn.microsoft.com/es-es/dotnet/csharp/linq/
https://www.netmentor.es/entrada/linq-csharp

Entity Framework: un ORM para .NET

Para los programadores Java que han usado Hibernate o cualquier otro ORM sabrán que son herramientas muy útiles para el mapeo de tablas y clases Java. Pues ahorran una gran cantidad de código y horas de esfuerzo.

.NET tiene un ORM llamado EntityFramework (EF), el cual es:

"... un mapeador objeto-relacional (ORM) de Microsoft para .NET que permite a los desarrolladores interactuar con bases de datos relacionales usando objetos de C# en lugar de escribir consultas SQL".

Un ORM nos permite manipular la información de las tablas como objetos. Esto nos evita tener que lidiar con SQL nativo.

Al usar EntityFramework podemos:

  • Mapear las tablas de nuestra base de datos como si fueran clases. 
  • Manipular la información de las tablas sin necesidad de sentencias SQL. 
  • Contar con las operaciones CRUD ya "integradas". 
  • Compatibilidad con LINQ (un lenguaje para realizar consultas con C#). 
  • Permitir conectarnos distintas fuentes de datos como MS SQL Server, MySQL, PostgreSQL, etc. 
  • Simplificar nuestro código.

También debemos tener cuidado de:

  1. Observar el rendimiento de las consultas. 
  2. Entender cómo se usa LINQ.

Al usar un ORM debemos entender:

  1. Qué base de datos vamos a usar. 
  2. Qué es lo que queremos mapear (tablas). 
  3. Convertir la estructura de las tablas a objetos (mapeo). 
  4. Qué operaciones vamos a realizar sobre esos objetos (operaciones CRUD).

Comenzando con EntityFramework

Crearemos una sencilla aplicación C# usando el ORM EntityFramework.

Requisitos:

  • Contar con la herramienta dotnet instalada. 
  • Contar con MariaDB instalado. 
  • Tener nociones de C#.

1. Es necesario contar con una base de datos ("cursomariadb") y al menos una tabla en la misma (persons).

CREATE OR REPLACE TABLE persons(
personid int, 
lastname varchar(255), 
firstname varchar(255), 
address varchar(255), 
city varchar(255)
);

3. Crearemos el proyecto con la herramienta dotnet y nos ubicaremos en el directorio creado:

$ dotnet new console -n DemoEFMariaDB
$ cd DemoEFMariaDB

4. Instalar los paquetes nugets necesarios:

$ dotnet add package Microsoft.EntityFrameworkCore --version 8.0.0
$ dotnet add package Pomelo.EntityFrameworkCore.MySql --version 8.0.0

Con estos instalamos EntityFramework y el conector a MariaDB (recordar que es un fork de MySQL).

5. Dentro del directorio del proyecto crearemos una carpeta llamada Model y dentro una clase C# que homologue a la tabla.

Person.cs

namespace Model
{
    public class Person
    {
        public int PersonId { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string Address { get; set; }

        public string City { get; set; }

        public Person()
        {
            PersonId = 0;
            LastName = string.Empty;
            FirstName = string.Empty;
            Address = string.Empty;
            City = string.Empty;
        }

    }

}

6. Ahora crearemos una clase contexto para la conexión a la base de datos.

AppDbContext.cs

using Microsoft.EntityFrameworkCore;
using Model;


public class AppDbContext : DbContext
{
    public DbSet<Person> Persons { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseMySql(
            "server=localhost;database=cursomariadb;user=root;password=secreta",
            new MySqlServerVersion(new Version(10, 11, 2)) 
        );
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Person>().ToTable("persons"); 
        modelBuilder.Entity<Person>().HasKey(p => p.PersonId);
    }
}

7. Modificaremos el programa principal del proyecto para agregar las operaciones CRUD. Comenzaremos con la operación INSERT INTO .

Program.cs

using System;
using System.Linq;
using Model;

class Program
{
    static void Main()
    {
        using var db = new AppDbContext();

        // INSERT
        var newPerson = new Person
        {
            PersonId = 4,
            FirstName = "Juan",
            LastName = "Pérez",
            Address = "Calle Falsa 123",
            City = "CDMX"
        };
        db.Persons.Add(newPerson);
        db.SaveChanges();
        Console.WriteLine("Insertado correctamente.");
    }

}

Seguiremos con la instrucción SELECT:

// SELECT
        var persons = db.Persons.ToList();
        Console.WriteLine("Listado de personas:");
        foreach (var p in persons)
        {
            Console.WriteLine($"{p.PersonId} - {p.FirstName} {p.LastName} - {p.City}");
        }

Ahora con la instrucción UPDATE:

 // UPDATE
        var personToUpdate = db.Persons.FirstOrDefault(p => p.PersonId == 4);
        if (personToUpdate != null)
        {
            personToUpdate.City = "Guadalajara";
            db.SaveChanges();
            Console.WriteLine("Actualizado correctamente.");
        }

Finalmente con la instrucción DELETE:

// DELETE
        var personToDelete = db.Persons.FirstOrDefault(p => p.PersonId == 4);
        if (personToDelete != null)
        {
            db.Persons.Remove(personToDelete);
            db.SaveChanges();
            Console.WriteLine("Eliminado correctamente.");
        }

Código completo:

using System;
using System.Linq;
using Model;

class Program
{
    static void Main()
    {
        using var db = new AppDbContext();

        // INSERT
        var newPerson = new Person
        {
            PersonId = 4,
            FirstName = "Juan",
            LastName = "Pérez",
            Address = "Calle Falsa 123",
            City = "CDMX"
        };
        db.Persons.Add(newPerson);
        db.SaveChanges();
        Console.WriteLine("Insertado correctamente.");

        // SELECT
        var persons = db.Persons.ToList();
        Console.WriteLine("Listado de personas:");
        foreach (var p in persons)
        {
            Console.WriteLine($"{p.PersonId} - {p.FirstName} {p.LastName} - {p.City}");
        }

        // UPDATE
        var personToUpdate = db.Persons.FirstOrDefault(p => p.PersonId == 4);
        if (personToUpdate != null)
        {
            personToUpdate.City = "Guadalajara";
            db.SaveChanges();
            Console.WriteLine("Actualizado correctamente.");
        }

        // DELETE
        var personToDelete = db.Persons.FirstOrDefault(p => p.PersonId == 4);
        if (personToDelete != null)
        {
            db.Persons.Remove(personToDelete);
            db.SaveChanges();
            Console.WriteLine("Eliminado correctamente.");
        }
    }
}

8. Ejecutamos la aplicación:

$ dotnet run

Salida:

Insertado correctamente.
Listado de personas:
1 - Carmen Moreno - EDOMEX
2 - Jimena Torres - Toluca
3 - Torres Manuel - Cuajimalpa
4 - Juan Pérez - CDMX
Actualizado correctamente.

¡Hemos creado una aplicación C# con EntityFramework !

Conclusiones:

  • EntityFramework es una herramienta poderosa que nos ahorra tiempo de desarrollo simplificando el acceso datos y su manipulación. 
  • LINQ hace que las operaciones sobre las tablas sea más sencillo.
  • Su soporte a varias fuentes de datos es muy útil y no nos limita a usar una sola.
  • La complejidad de código se reduce abruptamente, ¡parece magia!
  • Aunque tiene muchas ventajas hay que considerar el rendimiento cuando existe una gran cantidad de datos.

Seguiremos con esta serie sobre C#.

Enlaces:

https://andresledo.es/csharp/entity-framework-core/
https://medium.com/@ravipatel.it/a-beginners-guide-to-entity-framework-core-ef-core-5cde48fc7f7a
https://codemonkeyjunior.blogspot.com/2025/08/programando-en-c-no-9-usando-nuget.html
https://codemonkeyjunior.blogspot.com/2019/06/instalar-paquetes-nuget-con-dotnet.html
https://es.wikipedia.org/wiki/Language_Integrated_Query
https://blog.georgekosmidis.net/using-mariadb-in-an-aspnet-core-api-with-entity-framework-core.html
https://www.entityframeworktutorial.net/
https://www.learnentityframeworkcore.com/
https://learn.microsoft.com/es-es/ef/

domingo, 23 de noviembre de 2025

WebSocket en un vistazo

WebSocket es un protocolo que permite la comunicación bidireccional y persistente en tiempo real entre un cliente (como un navegador web) y un servidor a través de una única conexión TCP.

A diferencia del modelo de solicitud-respuesta de HTTP, WebSocket mantiene la conexión abierta para que tanto el cliente como el servidor puedan enviar datos en cualquier momento, lo que lo hace ideal para aplicaciones que requieren actualizaciones dinámicas constantes, como chats en vivo, juegos multijugador o feeds financieros.

Características principales

  • Comunicación bidireccional: Tanto el cliente como el servidor pueden iniciar y enviar mensajes sin que el otro tenga que solicitarlos primero. 
  • Conexión persistente: Una vez que se establece la conexión, esta permanece activa hasta que el cliente o el servidor la cierren explícitamente. 
  • Eficiencia en tiempo real: Es más rápido que HTTP para comunicaciones en tiempo real porque no necesita establecer una nueva conexión para cada mensaje, reduciendo la latencia.

¿Cómo funciona?

  • Establecimiento de la conexión: Primero, se utiliza un protocolo de enlace (a menudo a través de una solicitud HTTP) para "actualizar" la conexión de HTTP a WebSocket
  • Comunicación full-duplex: Una vez establecida la conexión, los datos pueden fluir en ambas direcciones simultáneamente sobre la misma conexión TCP. 
  • Envío y recepción: El servidor puede "empujar" información al cliente (como una nueva notificación), y el cliente puede enviar datos al servidor, todo ello sin interrupciones.

Casos de uso comunes

  • Aplicaciones de chat en vivo: Permite la comunicación instantánea entre usuarios. 
  • Juegos en línea multijugador: Facilita la actualización en tiempo real de las posiciones de los jugadores y otras acciones del juego. 
  • Edición colaborativa de documentos: Muestra los cambios de los usuarios en tiempo real en un documento compartido. 
  • Paneles y monitoreo en tiempo real: Permite mostrar datos que cambian constantemente sin recargar la página. 
  • Notificaciones: Muestra alertas y actualizaciones de eventos sin que el usuario tenga que buscar activamente.

Explicando a un programador Java y C#:

  • Java: Piensa en un Socket clásico, pero optimizado para la web y con soporte nativo en servidores como Tomcat/Jetty
  • C#: Piensa en un Hub de SignalR donde los clientes están siempre conectados y puedes "emitir" mensajes como si fueran eventos.

Comparando WebSocket vs HTTP

Resumen de diferencias clave
Atributo HTTP tradicional WebSocket
Conexión Se abre y cierra por request Persistente tras el handshake
Dirección Unidireccional (cliente → servidor) Bidireccional (cliente ↔ servidor)
Latencia Mayor por overhead Menor, canal abierto
Estado Stateless Stateful (sesión viva)
Casos de uso APIs REST, páginas Chat, notificaciones, trading en tiempo real

Concluyendo, los WebSocket son como una línea telefónica abierta entre cliente y servidor:

  • En Java los manejas con @ServerEndpoint
  • En C# los usas fácilmente con SignalR
  • Ambos lenguajes permiten comunicación en tiempo real sin necesidad de abrir/cerrar conexiones constantemente.

Enlaces:

https://www.ibm.com/docs/es/was/9.0.5?topic=applications-websocket


Programando en C# no. 12 (ASP .NET Core)

En está ocasión crearemos un proyecto webapi con ASP .NET Core mediante la herramienta ``dotnet``. Si no la tienes instalada puedes ir a este sitio:

https://dotnet.microsoft.com/es-es/download

¿Qué haremos? Un servicio API para obtener el nuevo sueldo de un empleado de acuerdo a estos datos de entrada: id, nombre, sueldo base, edad, género y estado civil. Por el momento no usaremos alguna base de datos. Las condiciones a cumplir para el nuevo sueldo son las siguientes:

IF estado_civil == "casado" THEN
   bono25Porciento();
ELSE
   bono10Porciento();
END IF;

IF sueldo_base <= 20000 THEN
   aumentoSueldo25Porciento();
ELSE
   IF sueldo_base > 25000 THEN
      aumentoSueldo5Porciento();
   END IF;
END IF;

El servicio tomará los datos del usuario (id, nombre, sueldo base, edad y género) y con base a las condiciones dará un aumento. Como se puede ver no tiene mucha ciencia entender lo que queremos realizar.

Abrimos una terminal y creamos el proyecto:

$ dotnet new web -o SueldoNuevo
$ cd SueldoNuevo
$ code -r ../SueldoNuevo

Esto nos creará el proyecto, nos ubicará en el directorio y abrirá Visual Studio Code. Abrimos el programa principal y lo editamos:

Program.cs

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Sueldo nuevo en C#");

app.Run();

Ejecutamos con este comando:

$ dotnet run

Abrimos el navegador en la ruta: http://localhost:5048

Salida:

Sueldo nuevo en C#

Como queremos hacer una aplicación modular, crearemos una carpeta Model y ahí tendremos una clase llamada Empleado.cs.

using System;

namespace Model
{
    public class Empleado
    {
        public int Id
        {
            get;set;
        }

        public string Nombre
        {
            get; set;
        }

        public int Edad
        {
            get; set;
        } 

        public double SueldoBase
        {
            get; set;
        }

        public bool isCasado
        {
            get;set;
        }

        public string Genero
        {
            get; set;
        }

        public Empleado()
        {
            Id = 0;
            Nombre = string.Empty;
            SueldoBase = 0.0;
            Edad = 0;
            isCasado = false;
            Genero = string.Empty;
        }
    }
}

Ahora creamos un directorio denominado como Services. Dentro crearemos una interface llamada IEmpleadoService.cs y una clase que la implementa llmada EmpleadoService.cs.

IEmpleadoService.cs

using System;
using Model;

namespace Services
{
    public interface IEmpleadoService
    {
        double GetNuevoSueldo(Empleado empleado);
    }
}

EmpleadoService.cs

using System;
using Model;

namespace Services
{
    public class EmpleadoService: IEmpleadoService
    {
        public double GetNuevoSueldo(Empleado empleado)
        {
            var sueldoFinal = empleado.SueldoBase;
            if (empleado.isCasado)
            {
                sueldoFinal += empleado.SueldoBase + 0.25;
            }else
            {
                sueldoFinal += empleado.SueldoBase + 0.10;
            }

            if (empleado.SueldoBase <= 20000)
            {
                sueldoFinal += empleado.SueldoBase + 0.25;
            }else if (empleado.SueldoBase > 25000)
            {
                sueldoFinal += empleado.SueldoBase + 0.05;
            }else
            {
                sueldoFinal += empleado.SueldoBase + 0.01;
            }
            
            return sueldoFinal;
        }
    }
}

Ahora implementaremos la lógica descrita en el pseudo código de arriba. Modificamos el programa principal:

Program.cs

using Model;
using Services;

var builder = WebApplication.CreateBuilder(args);

// Registro del servicio en el contenedor de DI
builder.Services.AddScoped<IEmpleadoService, EmpleadoService>();

var app = builder.Build();

app.MapGet("/", () => "Sueldo nuevo en C#");

// Endpoint que usa el servicio inyectado
app.MapPost("/calcular", (Empleado empleado, IEmpleadoService empleadoService) =>
{
    double nuevoSueldo = empleadoService.GetNuevoSueldo(empleado);
    return new
    {
        empleado.Nombre,
        SueldoBase = empleado.SueldoBase,
        SueldoFinal = nuevoSueldo
    };
});

app.Run();

¿Qué podemos notar en este programa?

  • Hemos inyectado la clase servicio (EmpleadoService) de manera similar a lo que se hace con @Autowired de Spring Boot. 
  • Hemos creado un endpoint con la lógica del servicio inyectado.

Para hacer una petición al servicio crearemos un script con Powershell:

nuevosueldo.ps1

$body = @{
    Id = 1
    Nombre = "Pedro Ramírez"
    Edad = 35
    SueldoBase = 18000
    isCasado = $true
    Genero = "M"
} | ConvertTo-Json

Invoke-RestMethod -Uri "http://localhost:5048/calcular" `
    -Method Post `
    -ContentType "application/json" `
    -Body $body

Si tenemos curl, podemos hacerlo de esta forma:

curl -X POST http://localhost:5048/calcular \
  -H "Content-Type: application/json" \
  -d '{
    "Id": 1,
    "Nombre": "Pedro Ramírez",
    "Edad": 35,
    "SueldoBase": 18000,
    "isCasado": true,
    "Genero": "M"
  }'

Ejecutamos con este comando:

$ dotnet run

Ejecutamos el script:

$ .\nuevosueldo.ps1 

Si todo va bien, la salida será la siguiente:

nombre        sueldoBase sueldoFinal
------        ---------- -----------
Pedro Ramírez      18000     54000.5

UPDATE: También podemos hacer peticiones con una archivo *.http. Crearemos uno en el directorio principal y lo nombraremos MyRequests.http. Tendrá el siguiente contenido:

POST http://localhost:5048/calcular
Content-Type: application/json

{
  "Id": 1,
  "Nombre": "Pedro Ramírez",
  "Edad": 35,
  "SueldoBase": 18000,
  "isCasado": true,
  "Genero": "M"
}


GET http://localhost:5048/

Si todo va bien, veremos este resultado:

¡Hemos creado un sencillo servicio api con C#!

Conclusión:

Creamos una aplicación webapi con C# desde la herramienta dotnet.

Aprendimos como se puede dividir la aplicación modularmente (directorios Model y Services).

Como implementar métodos de una interface C# (IEmpleadoService.cs y EmpleadoService.cs)

Como usar inyección de dependencia similar a lo que se hace con Spring Boot.

Como hacer una petición al servicio usando un script de Powershell y/o curl. Además del archivo *.http

Lo que sigue es usar una Base de datos (MS SQL Server, Maria DB, PostrgeSQL, etc.) y tablas para replicar esa funcionalidad, pero lo haremos en otra ocasión.

Enlaces:

https://learn.microsoft.com/es-es/aspnet/core/tutorials/min-web-api?view=aspnetcore-10.0&tabs=visual-studio-code

https://dotnet.microsoft.com/es-es/apps/aspnet/apis

LINQ: un lenguaje de consulta para C#

LINQ ( Language Integrated Query ) es una característica de C# que permite consultar y manipular datos directamente desde el lenguaje, ...

Etiquetas

Archivo del blog