← maurobernal.com.ar

La historia de .NET: Framework, Core y la unificación en .NET 5+

Si alguna vez te confundiste entre .NET Framework, .NET Core y simplemente .NET, no sos el único. Microsoft tomó decisiones de naming que generaron (y siguen generando) confusión, pero cada versión tiene una historia clara y razones de peso detrás. Vamos por partes.

🏛️ .NET Framework: el pionero (2002–2019)

¿Qué era?

.NET Framework llegó en 2002 como la respuesta de Microsoft a Java. Era un entorno de ejecución administrado (managed runtime) con un compilador JIT, una biblioteca de clases enorme (BCL) y el concepto de CLR (Common Language Runtime). Corría exclusivamente en Windows, y eso nunca cambió.

Versiones clave:

  • 1.0 (2002): El debut. C# como lenguaje estrella, ASP.NET Web Forms, ADO.NET.
  • 2.0 (2005): Generics, métodos anónimos, nullable types. Un salto enorme.
  • 3.0 (2006): WPF, WCF, WF (Windows Workflow Foundation), CardSpace.
  • 3.5 (2007): LINQ, Entity Framework (preview), extensión de C# 3.0.
  • 4.0 (2010): TPL (Task Parallel Library), dynamic, MEF, contratos de código.
  • 4.5 / 4.6 / 4.7 (2012–2017): async/await, mejoras de performance, soporte HTTP/2.
  • 4.8 (2019): La última versión. Aún recibe parches de seguridad, pero no evoluciona.

¿Qué trajo de bueno?

  • Madurez y estabilidad en ecosistemas empresariales.
  • Integración nativa con COM, Windows APIs, IIS.
  • Una BCL ridículamente completa para el año que era.
  • WPF para desktop y ASP.NET Web Forms / MVC para web.

¿Qué tenía de malo?

  • Solo Windows. Punto.
  • Instalado a nivel sistema operativo: actualizar podía romper apps ya instaladas.
  • Código cerrado, sin contribuciones de la comunidad.
  • Performance mediocre comparado con alternativas modernas.

Ejemplo .NET Framework 4.7 — async/await

using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        using var client = new HttpClient();
        string result = await client.GetStringAsync("https://api.github.com");
        Console.WriteLine(result.Substring(0, 100));
    }
}

Perfecto en .NET Framework 4.5+, pero solo corre en Windows.


🚀 .NET Core: la revolución open source (2016–2020)

¿Por qué nació?

Para 2013, el mundo había cambiado. Linux dominaba los servidores, Docker empezaba a despegar, Node.js y Go demostraban que podías tener performance sin sacrificar productividad. Microsoft, bajo la gestión de Satya Nadella, tomó una decisión histórica: reescribir .NET desde cero, open source y multiplataforma.

En 2016 llegó .NET Core 1.0. No era compatible con todo lo de Framework, pero era libre, rápido y corrías en Windows, Linux y macOS.

Versiones clave:

  • 1.0 / 1.1 (2016): MVP. ASP.NET Core, básico pero multiplataforma.
  • 2.0 / 2.1 (2017–2018): .NET Standard 2.0 para compartir código entre Framework y Core. 2.1 fue LTS.
  • 2.2 (2018): Mejoras en routing y health checks en ASP.NET Core.
  • 3.0 (2019): Blazor Server, gRPC, Worker Services, WPF y WinForms en Windows.
  • 3.1 (2019): LTS. La última versión de «Core». Soporte extendido hasta diciembre 2022.

¿Qué trajo?

  • ✅ Multiplataforma real: Windows, Linux, macOS.
  • ✅ Open source: código en GitHub, PRs de la comunidad.
  • ✅ Side-by-side: podés tener múltiples versiones instaladas sin conflictos.
  • ✅ Performance: Kestrel como servidor web integrado, mucho más rápido que IIS.
  • ✅ Contenedores: imágenes Docker oficiales de Microsoft.
  • ✅ Diseño modular: instalás solo lo que necesitás (NuGet packages).

¿Qué sacrificó?

  • ❌ Web Forms (nunca llegó a Core).
  • ❌ WCF completo (solo cliente, no servidor).
  • ❌ Algunas APIs de Windows-only que no tenía sentido portar.

Ejemplo: ASP.NET Core minimal API (3.1+)

// Startup.cs en .NET Core 3.1
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

// HomeController.cs
[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
    [HttpGet]
    public string Get() => "Hola desde .NET Core 3.1";
}

Este mismo código corre idéntico en Windows, Ubuntu o un contenedor Docker.

El factor Docker

FROM mcr.microsoft.com/dotnet/aspnet:3.1
WORKDIR /app
COPY . .
ENTRYPOINT ["dotnet", "MiApp.dll"]

Imposible en .NET Framework. Natural en .NET Core.


🔮 .NET 5 en adelante: la unificación (2020–presente)

¿Por qué no fue .NET Core 4.0?

Buena pregunta. .NET Core 4.x hubiera generado confusión directa con .NET Framework 4.x que seguía vivo. Microsoft eligió saltar a .NET 5 para marcar un punto de quiebre claro: esto ya no es ni Framework ni Core. Es simplemente .NET.

La promesa: una sola plataforma para todo. Web, desktop, mobile (via MAUI), cloud, IoT, ML.

Versiones desde .NET 5:

  • .NET 5 (2020): Unificación. Records, pattern matching mejorado, source generators.
  • .NET 6 (2021) — LTS: Minimal APIs, MAUI preview, Hot Reload, DateOnly/TimeOnly.
  • .NET 7 (2022): Rate limiting, mejoras en Regex source gen, Native AOT.
  • .NET 8 (2023) — LTS: Blazor unificado, Primary constructors en C# 12, Native AOT estable.
  • .NET 9 (2024): Performance extrema (LINQ, colecciones), mejoras en AI integrations.
  • .NET 10 (2025 — LTS): En desarrollo activo al momento de escribir esto.

Lo que cambió con .NET 5+

Minimal APIs (.NET 6)

// Program.cs — .NET 6+
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hola desde .NET 6!");
app.MapGet("/productos/{id}", (int id) => new { Id = id, Nombre = "Laptop" });

app.Run();

Menos boilerplate, mismo poder. No más Startup.cs obligatorio.

Records (C# 9 / .NET 5)

// Antes: clase con todo el boilerplate
public class Producto
{
    public int Id { get; init; }
    public string Nombre { get; init; }
    
    public Producto(int id, string nombre) { Id = id; Nombre = nombre; }
    public override bool Equals(object? obj) => /* ... mucho código ... */
}

// Con records: una sola línea
public record Producto(int Id, string Nombre);

// Uso
var p1 = new Producto(1, "Laptop");
var p2 = p1 with { Nombre = "Monitor" }; // copia inmutable con cambio
Console.WriteLine(p1 == p2); // false (compara por valor)

Native AOT (.NET 7/8)

Compilación a binario nativo sin necesidad del runtime. Arranque ultra-rápido, ideal para lambdas y microservicios.

<!-- En el .csproj -->
<PropertyGroup>
  <PublishAot>true</PublishAot>
</PropertyGroup>
dotnet publish -r linux-x64 -c Release

El resultado: un único ejecutable de ~10MB que arranca en milisegundos, sin JIT, sin runtime instalado.


📊 Comparativa directa

Característica.NET Framework.NET Core.NET 5+
PlataformasSolo WindowsWin / Linux / macOSWin / Linux / macOS
Open Source
Side-by-side
ContenedoresLimitado✅ Nativo✅ Nativo
Web Forms
WPF / WinFormsSolo Windows (3.0+)Solo Windows
WCF Server❌ (CoreWCF community)
BlazorServer (3.0)✅ Server + WASM + Hybrid
MAUI / Xamarin✅ (.NET 6+)
PerformanceBuenaExcelenteExcepcional
Ciclo de vidaMantenimientoEOL (3.1 = dic 2022)Activo (LTS cada 2 años)

🤔 ¿Y .NET Standard?

En el medio de la transición existió .NET Standard: una especificación de APIs que garantizaba que una biblioteca que la targeteara funcionara tanto en Framework como en Core. No era una implementación, era un contrato.

Con la unificación en .NET 5+, .NET Standard perdió relevancia. Si tu biblioteca debe soportar Framework 4.x también, usá .NET Standard 2.0. Si solo usás .NET moderno, targetear directamente net6.0/net8.0 es lo correcto.

<!-- .csproj de una librería compatible con todo -->
<PropertyGroup>
  <TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
</PropertyGroup>

🛠️ ¿Qué usar hoy?

  • Proyecto nuevo: net9.0 o net8.0 (LTS). Sin dudas.
  • Mantener sistema legacy en Framework: Seguí en Framework, actualizá gradualmente a .NET 8+.
  • Migración: Usá el Upgrade Assistant de Microsoft.
  • Librería que deba correr en ambos mundos: .NET Standard 2.0 + multi-target.

Conclusión

La historia de .NET es la historia de una plataforma que supo reinventarse. De los monolitos Windows-only a microservicios nativos en Linux. De código cerrado a uno de los proyectos open source más activos de GitHub. De performance «suficiente» a estar consistentemente entre los frameworks más rápidos del mundo según los benchmarks de TechEmpower.

La confusión de nombres fue el precio de una transición honesta: Microsoft no murió .NET Framework de un día para el otro, lo mantuvo vivo mientras construía algo mejor. Y cuando estuvo listo, lo llamó simplemente .NET.

Bienvenido al futuro del ecosistema.