Table Per Hierarchy TPH en EF Core herencia sin JOINs

Table Per Hierarchy (TPH) en EF Core

Table Per Hierarchy EF Core (TPH): herencia sin JOINs en una sola tabla

Table Per Hierarchy (TPH) en EF Core es la estrategia de herencia por defecto en Entity Framework Core.

Permite mapear una jerarquía completa de clases —tipo base y derivados— en una sola tabla, utilizando una columna discriminadora para identificar el tipo concreto de cada fila.

En esta guía verás cómo funciona TPH en EF Core, cómo se configura, qué esquema genera, el SQL real que produce, sus implicaciones de rendimiento y en qué escenarios conviene usarlo frente a TPT o TPC.

A lo largo del artículo se incluyen ejemplos reproducibles y videos técnicos curados a partir de recursos especializados como LearnEntityFrameworkCore, con tiempos exactos para profundizar en cada concepto.

También analizaremos sus ventajas reales, limitaciones prácticas y patrones de uso habituales en APIs y sistemas donde el rendimiento de lectura y las consultas polimórficas son clave.


🧠 ¿Qué hace exactamente Table Per Hierarchy EF Core (TPH)?

La forma más simple de entender Table Per Hierarchy es este modelo mental:

TPH = “Guarda toda la jerarquía de herencia en una sola tabla y usa un discriminador para saber qué tipo es cada fila”.

Si estás modelando dominios reales en EF Core, TPH suele combinarse con relaciones entre entidades. En ese caso, te puede servir esta guía paso a paso sobre cómo relacionar tablas en EF Core con SQL Server.

  • Toda la jerarquía se almacena en una única tabla.
  • Una columna discriminadora identifica el tipo concreto.
  • No se generan JOINs al consultar.
  • Las propiedades que no aplican a un tipo quedan como NULL.
  • EF Core materializa cada fila en el tipo correcto automáticamente.

Este enfoque es ideal para escenarios donde necesitas consultas polimórficas rápidas,
minimizar complejidad SQL y evitar el costo de múltiples JOINs.


🧪 Demo interactiva: Table Per Hierarchy (TPH) en EF Core

Esta demo interactiva muestra cómo Entity Framework Core mapea una jerarquía de herencia utilizando Table Per Hierarchy (TPH) y cómo el discriminador determina el tipo concreto materializado, todo desde una única tabla y sin JOINs.

En el ejemplo se define una clase base abstracta y varios tipos derivados. Al ejecutar consultas polimórficas o consultas por tipo derivado, EF Core genera un único SELECT con un filtro por discriminador para materializar correctamente cada entidad.

🔗 Código completo en .NET Fiddle:
https://dotnetfiddle.net/jlelwc

Puedes ejecutar el código, modificar el modelo y observar el comportamiento de TPH en tiempo real, incluyendo la consulta polimórfica, la consulta por tipo derivado y el SQL generado sin JOINs.


🧩 Reglas esenciales de Table Per Hierarchy (TPH) en EF Core

Antes de usar TPH en un proyecto real, es importante entender sus reglas internas. Estas reglas derivan directamente de cómo EF Core traduce la herencia hacia el modelo relacional
y cómo materializa los resultados.

  • Toda la jerarquía se almacena en una sola tabla.
  • Existe siempre una columna discriminadora que identifica el tipo concreto.
  • No se generan JOINs al consultar tipos derivados o el tipo base.
  • Las propiedades que no aplican a un tipo se almacenan como NULL.
  • Agregar un nuevo tipo derivado suele implicar nuevas columnas nullable.
  • El tipo base puede ser abstracto o concreto.
  • Todas las entidades comparten la misma clave primaria.

TPH prioriza simplicidad y rendimiento de lectura, a costa de producir tablas más anchas y menos normalizadas.


💻 Ejemplo básico – Table Per Hierarchy EF Core (TPH)

El patrón más común de Table Per Hierarchy EF Core (TPH) consiste en una clase base (opcionalmente abstracta), varios tipos derivados y un discriminador que le indica a EF Core cómo materializar cada fila.

public abstract class Contract
{
    public int ContractId { get; set; }
    public DateTime StartDate { get; set; }
    public int Months { get; set; }
    public decimal Charge { get; set; }
}

public class MobileContract : Contract
{
    public string MobileNumber { get; set; } = default!;
}

public class TvContract : Contract
{
    public string PackageType { get; set; } = default!;
}

public class BroadbandContract : Contract
{
    public int DownloadSpeed { get; set; }
}

⚙️ Configuración explícita del discriminador (recomendado)

Aunque EF Core usa TPH por convención, configurar el discriminador explícitamente te da control total sobre el esquema y los valores.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Contract>()
        .ToTable("Contracts")
        .HasDiscriminator<int>("ContractType")
        .HasValue<MobileContract>(1)
        .HasValue<TvContract>(2)
        .HasValue<BroadbandContract>(3);
}

Nota: si el tipo base no es abstracto, también debe tener un valor de discriminador.


🧾 SQL generado (consulta por tipo derivado)

var mobileContracts = context.Set<Contract>()
    .OfType<MobileContract>()
    .ToList();
SELECT [c].[ContractId], [c].[StartDate], [c].[Months], [c].[Charge], [c].[MobileNumber]
FROM [Contracts] AS [c]
WHERE [c].[ContractType] = 1;

⚖️ TPH vs TPT vs TPC (comparativa rápida)

Estrategia Esquema SQL típico Trade-off principal
TPH Una sola tabla SELECT con filtro Tabla ancha, NULLs
TPT Tabla base + derivadas JOINs Costo en lectura
TPC Tabla por tipo concreto UNION ALL Duplicación

🎬 Videos recomendados – Table Per Hierarchy EF Core (TPH)

Los siguientes videos fueron seleccionados por su claridad técnica y por ofrecer una explicación profunda del mapeo de herencia en Entity Framework Core.
Son especialmente útiles para entender por qué TPH es la estrategia por defecto, cómo se traduce a SQL y qué implicaciones reales tiene frente a TPT y TPC.


1) .NET Data Community Standup — Inheritance Mapping en EF Core

En esta sesión oficial de la comunidad .NET, Arthur Vickers explica cómo EF Core mapea jerarquías de herencia internamente, poniendo énfasis en la generación de SQL, el uso del discriminador y los trade-offs de rendimiento. Es una referencia clave para entender por qué TPH suele ofrecer el mejor desempeño en lectura.


  • 09:00
    — Concepto de TPH y columna discriminadora
  • 15:00 — Consultas sin JOINs
  • 26:30 — Comparación de rendimiento: TPH vs TPT
  • 42:00 — Uso de columnas dispersas (SQL Server)

2) Inheritance en EF Core: TPH vs TPT vs TPC con ejemplos reales

Este video muestra el mapeo de herencia utilizando modelos concretos y migraciones reales. La sección dedicada a TPH ilustra claramente cómo una sola tabla simplifica las consultas, a cambio de introducir columnas NULL cuando las propiedades no aplican a todos los tipos.


  • 03:08
    — Tabla única con discriminador
  • 04:31 — Trade-off: columnas NULL
  • 06:31 — Configuración explícita del discriminador
  • 09:31 — Errores comunes con clases base abstractas

3) Herencia en EF Core 7: visión general

Una introducción de alto nivel a las estrategias de herencia en EF Core 7, mostrando cómo TPH se aplica por defecto y cómo el discriminador
impacta directamente en el esquema generado y el comportamiento de las consultas.


  • 04:30
    — Mapeo TPH por defecto
  • 05:00 — Discriminador y valores NULL
  • 11:45 — Configuración explícita de estrategias
  • 12:30 — Conclusiones y recomendaciones

❓ Preguntas frecuentes (FAQ): Table Per Hierarchy EF Core (TPH)

Respuestas técnicas y directas a dudas comunes al implementar Table Per Hierarchy (TPH) en proyectos reales con EF Core.

🧩 ¿TPH es la estrategia por defecto en EF Core?

Sí. Por convención, EF Core mapea herencia usando TPH salvo que configures explícitamente otra estrategia (como TPT o TPC).
Toda la jerarquía se representa en una sola tabla con un discriminador.

⚙️ ¿Qué es el discriminador y por qué es obligatorio en TPH?

El discriminador es una columna que indica el tipo concreto de cada fila. EF Core lo usa para materializar cada registro como la clase correcta al ejecutar consultas.

🧠 ¿TPH genera JOINs al consultar entidades derivadas?

No. TPH consulta una sola tabla. Normalmente verás un SELECT único con filtro por discriminador al consultar un tipo derivado.

🔗 ¿Qué pasa con las columnas que no aplican a ciertos tipos?

La tabla incluye columnas para propiedades de toda la jerarquía. Cuando una propiedad no aplica a un tipo, esa columna queda como NULL.
Mientras más tipos y propiedades específicas tengas, más ancha y dispersa se vuelve la tabla.

⚡ ¿Cuándo suele ser mejor TPH que TPT?

Cuando haces consultas polimórficas con frecuencia y quieres evitar el costo de JOINs.
TPT prioriza normalización, pero suele pagar con SQL más complejo y más costo de lectura.

🚨 ¿Cuál es el riesgo real de TPH en producción?

El riesgo típico no es correctitud, sino crecimiento del esquema: tabla muy ancha, muchas columnas NULL y migraciones frecuentes si la jerarquía cambia seguido.


⚠️ Advertencias importantes

Antes de usar TPH en un entorno real, conviene tener en cuenta estas limitaciones.
Son puntos derivados del trade-off central: performance y simplicidad a cambio de una tabla más ancha.

  • A medida que agregas tipos derivados, es común terminar con una tabla muy ancha y muchas columnas NULL.
  • Si la jerarquía cambia con frecuencia, la tabla puede requerir migraciones constantes (nuevas columnas, cambios de tipo, etc.).
  • El discriminador es crítico: define valores estables y explícitos para evitar sorpresas con refactors.
  • Si necesitas un esquema altamente normalizado por políticas internas, TPH probablemente no sea la mejor opción.

🧾 Checklist de repaso

Usa esta lista como referencia rápida antes de elegir Table Per Hierarchy (TPH). Reúne los puntos esenciales para asegurar un diseño mantenible sin sacrificar rendimiento.

  • ✔️ Confirma que tu jerarquía será relativamente estable.
  • ✔️ Define un discriminador explícito (HasDiscriminator + HasValue) con valores estables.
  • ✔️ Revisa cuántas propiedades específicas por tipo se traducen en columnas NULL.
  • ✔️ Prioriza TPH si haces consultas polimórficas frecuentes y quieres evitar JOINs.
  • ✔️ Considera TPT si la normalización pesa más que el performance de lectura.
  • ✔️ Considera TPC si quieres evitar JOINs y evitar NULL aceptando duplicación.

🔗 Recursos recomendados

Documentación oficial, guías prácticas y referencias complementarias para profundizar en herencia en EF Core y comparar TPH vs TPT vs TPC.

Tip: guarda estos enlaces para tener a mano documentación oficial y referencias rápidas cuando estés decidiendo una estrategia de herencia según tus patrones de consulta.

🏁 Conclusión

Table Per Hierarchy EF Core (TPH) es la estrategia más simple y, en muchos escenarios, la más rápida para mapear herencia en EF Core.

En resumen:
  • Usa TPH si priorizas rendimiento de lectura y SQL simple (sin JOINs).
  • Evalúa TPT si tu prioridad es normalización, aceptando el costo de JOINs.
  • Evalúa TPC si quieres evitar JOINs y NULL, aceptando duplicación.

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll to Top