C# & .NET Core: Gu铆a Definitiva Extendida
Documentaci贸n oficial de arquitectura, Clean Code y rendimiento para el desarrollo avanzado en Devlinks.
1. Introducci贸n a .NET
C# es un lenguaje manejado ("managed code") que corre en el CLR (Common Language Runtime). En las versiones modernas (.NET 6/7/8), se caracteriza por su alt铆simo rendimiento, uso extensivo de asincron铆a y compilaci贸n AOT (Ahead-of-Time).
Target Framework: En Devlinks estandarizamos el uso de .NET 8 (LTS) por sus mejoras masivas de performance en JIT (Just-In-Time) y nuevas capacidades de C# 12 (Primary constructors, Collection expressions).
2. Fundamentos y Tipado
Comprender la diferencia entre Tipos por Valor y Tipos por Referencia es cr铆tico para evitar presionar al Garbage Collector (GC).
Value Types vs Reference Types
// Value Types (Structs, ubicados en el STACK - R谩pidos)
int age = 28;
DateTime date = DateTime.Now;
Guid id = Guid.NewGuid();
// Reference Types (Classes, ubicados en el HEAP - Recolectados por el GC)
string name = "Devlinks";
List<int> numbers = new();
// Tipado Impl铆cito Moderno (Inferencia de tipo al inicializar)
var user = new UserService();
3. Programaci贸n Orientada a Objetos
Records & Primary Constructors (C# 12)
// Los records son inmutables por defecto. Se eval煤an por valor. Excelente para DTOs.
public record UserDto(int Id, string FirstName, string LastName);
// Uso de 'with' para la mutaci贸n no destructiva
var user1 = new UserDto(1, "Angel", "Tapia");
var user2 = user1 with { FirstName = "Devlinks" };
// Primary Constructors en Clases Regulares
public class UserRepository(AppDbContext dbContext)
{
private readonly AppDbContext _db = dbContext;
// ...
}
Interfaces vs Abstract Classes
Usa interfaces para definir *contratos de comportamiento*, y Abstract Classes para *compartir implementaci贸n y estado central*.
public interface IEntity
{
int Id { get; set; }
}
4. Manejo de Excepciones y Clean Code
Global Error Handler Middleware
Evita llenar tus controladores de try/catch usando un Global Exception Handler mediante middleware.
// Evitar (Anti-Patr贸n):
try {
ProcesarData();
} catch (Exception ex) {
return StatusCode(500, ex.Message);
}
// Mejor: Lanzar excepciones controladas o devolver Result Patters y atrapar los errores 500 Globalmente.
throw new ValidationException("Datos inv谩lidos");
5. Colecciones y LINQ Avanzado
Consultas Complejas Seguras
var activeUsers = users
.Where(u => u.IsActive && u.Age >= 18)
.OrderByDescending(u => u.CreatedAt)
.GroupBy(u => u.Department)
.Select(group => new {
Department = group.Key,
Count = group.Count(),
LatestUser = group.First()
})
.ToList();
Siempre recuerda que IEnumerable retrasa la ejecuci贸n (Lazy Evaluation). Si vas a iterar una colecci贸n de bases de datos varias veces, aseg煤rate de aplicar un .ToList() o .ToArray() para materializar los resultados.
6. Pattern Matching Avanzado
Switch con condiciones relacionales y de propiedad
decimal descuento = invoice switch
{
{ TotalAmount: > 1000 } => 0.15m,
{ TotalAmount: > 500 } and { IsB2B: true } => 0.10m,
_ => 0.0m
};
// Type Checking
if (obj is UserDto dto)
{
Console.WriteLine(dto.FirstName);
}
7. Programaci贸n As铆ncrona (Avanzada)
Para prevenir llamadas zombis, env铆a siempre el CancellationToken a lo largo del stack de ejecuci贸n. Utiliza ValueTask si el m茅todo suele retornar valores locales sin requerir el hilo.
Task vs ValueTask y Cancellation
// Firma correcta para APIs modernas
public async ValueTask<UserProfile> FetchProfileAsync(int id, CancellationToken ct)
{
// Pasando el CancellationToken al EF o HttpClient:
return await _db.Profiles.FirstOrDefaultAsync(p => p.Id == id, ct);
}
Llamadas en Paralelo con Task.WhenAll
var taskUsers = _db.Users.ToListAsync(ct);
var taskRoles = _db.Roles.ToListAsync(ct);
// Se procesan ambas consultas al mismo tiempo en diferentes hilos I/O
await Task.WhenAll(taskUsers, taskRoles);
var roles = taskRoles.Result; // Aqu铆 s铆 es l铆cito porque ya finaliz贸 WhenAll()
8. Inyecci贸n de Dependencias Superior
Patr贸n Factory y Keyed Services (.NET 8)
// Los Keyed Services evitan tener que inyectar IEnumerable<IType> para discriminar implementaciones
builder.Services.AddKeyedScoped<IPaymentService, StripePaymentService>("Stripe");
builder.Services.AddKeyedScoped<IPaymentService, PaypalPaymentService>("PayPal");
// Consumo en Controller/Minimal API
public class PaymentController(
[FromKeyedServices("Stripe")] IPaymentService paymentService)
{
//...
}
9. Entity Framework Core y Base de Datos
Privilegiar el Fluent API sobre los Data Annotations para mantener las entidades limpias y desacopladas de la infraestructura.
Fluent API Configuration (IEntityTypeConfiguration)
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.ToTable("Usuarios");
builder.HasKey(u => u.Id);
builder.Property(u => u.Name)
.IsRequired()
.HasMaxLength(100);
// Indices para optimizaci贸n
builder.HasIndex(u => u.Email).IsUnique();
}
}
10. Arquitectura ASP.NET Core API
Arquitectura Limpia / Capas Recomendadas
// En Devlinks escalamos separando responsabilidades:
1. Devlinks.API -> (Presentaci贸n: Controladores/Endpoints)
2. Devlinks.Application-> (L贸gica de Negocio: Casos de uso, CQRS, MediatR, DTOs)
3. Devlinks.Domain -> (Core: Entidades de Base, Value Objects, Excepciones de Dominio)
4. Devlinks.Infra -> (Datos: AppDbContext, Repositorios, Servicios Externos)