En Entity Framework, la herencia permite modelar relaciones entre entidades de manera que una clase base puede ser extendida por una o más clases derivadas.
La herencia es un concepto fundamental en la programación orientada a objetos (POO) que permite a una clase derivar de otra, heredando sus propiedades y comportamientos.
Sin embargo, dado que las bases de datos relacionales no soportan directamente la herencia, Entity Framework utiliza estrategias de mapeo para representar estas relaciones en tablas.
En este artículo, vamos a ver estas estrategias, sus ventajas, desventajas y cuándo es apropiado utilizar cada una 👇.
Estrategias de herencia
Las tres estrategias principales son:
- TPH (Table Per Hierarchy): Todas las clases en una jerarquía de herencia se almacenan en una sola tabla.
- TPT (Table Per Type): Cada clase en la jerarquía de herencia se almacena en su propia tabla.
- TPC (Table Per Concrete Class): Cada clase concreta en la jerarquía de herencia se almacena en su propia tabla, pero las propiedades de la clase base se duplican en cada tabla.
Aquí tenéis un resumen de sus principales características.
Característica | TPH | TPT | TPC |
---|---|---|---|
Número de tablas | 🟢 1 | 🔴 N (una por cada tipo) | 🔴 N (una por cada clase) |
Rendimiento | 🟢 Alto | 🟡 Medio | 🟢 Alto |
Normalización | 🔴 Baja | 🟢 Alta | 🟡 Media |
Uso de espacio | 🔴 Ineficiente | 🟢 Eficiente | 🟡 Redundante |
Complejidad del esquema | 🟢 Simple | 🔴 Complejo | 🔴 Complejo |
Clases para los ejemplo
Para los ejemplos, vamos a suponer que tenemos una jerarquía de clases donde Animal
es la clase base y Perro
y Gato
son clases derivadas.
public class Animal
{
public int Id { get; set; }
public string Nombre { get; set; }
}
public class Perro : Animal
{
public string Raza { get; set; }
}
public class Gato : Animal
{
public bool TieneGarras { get; set; }
}
Ya perdonaréis la falta de originalidad de poner animales como ejemplo de herencia 😉
Table Per Hierarchy (TPH)
TPH (Table Per Hierarchy) es una estrategia en la que todas las clases en una jerarquía de herencia se almacenan en una sola tabla.
Esta tabla incluye una columna discriminadora que indica el tipo de entidad que representa cada fila.
En nuestro ejemplo, en TPH, todas estas entidades se almacenarían en una sola tabla llamada Animales
con una columna discriminadora:
CREATE TABLE Animales (
Id INT PRIMARY KEY,
Nombre NVARCHAR(100),
Raza NVARCHAR(100),
TieneGarras BIT,
Discriminador NVARCHAR(50)
);
- Una única tabla, con todas las columnas
- La columna
Discriminador
indicará si la fila corresponde a unPerro
o a unGato
.
Configuración de TPH en Entity Framework
Para configurar TPH en Entity Framework, puedes usar Fluent API en el método OnModelCreating
de tu DbContext
:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Animal>()
.HasDiscriminator<string>("Discriminador")
.HasValue<Perro>("Perro")
.HasValue<Gato>("Gato");
}
Table Per Type (TPT)
TPT (Table Per Type) es una estrategia en la que cada clase en la jerarquía de herencia se almacena en su propia tabla. Las tablas están relacionadas mediante claves foráneas.
Usando el mismo ejemplo de Animal
, Perro
y Gato
, en TPT tendríamos tres tablas:
CREATE TABLE Animales (
Id INT PRIMARY KEY,
Nombre NVARCHAR(100)
);
CREATE TABLE Perros (
Id INT PRIMARY KEY,
Raza NVARCHAR(100),
FOREIGN KEY (Id) REFERENCES Animales(Id)
);
CREATE TABLE Gatos (
Id INT PRIMARY KEY,
TieneGarras BIT,
FOREIGN KEY (Id) REFERENCES Animales(Id)
);
- Una tabla para cada clase (incluida la clase base)
- Cada una con sus propiedades (propiedades comunes en la clase base)
- Relaciones entre ellas
Configuración de TPT en Entity Framework
Para configurar TPT en Entity Framework, puedes usar Fluent API en el método OnModelCreating
:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Perro>().ToTable("Perros");
modelBuilder.Entity<Gato>().ToTable("Gatos");
}
Table Per Concrete Class (TPC)
TPC (Table Per Concrete Class) es una estrategia en la que cada clase concreta en la jerarquía de herencia se almacena en su propia tabla, pero las propiedades de la clase base se duplican en cada tabla.
Usando el mismo ejemplo de Animal
, Perro
y Gato
, en TPC tendríamos dos tablas:
CREATE TABLE Perros (
Id INT PRIMARY KEY,
Nombre NVARCHAR(100),
Raza NVARCHAR(100)
);
CREATE TABLE Gatos (
Id INT PRIMARY KEY,
Nombre NVARCHAR(100),
TieneGarras BIT
);
- Solo las clases derivadas tienen tabla
- Las tablas derivadas tienen las columnas comunes duplicadas
Configuración de TPC en Entity Framework
Para configurar TPC en Entity Framework, puedes usar Fluent API en el método OnModelCreating
:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Perro>().ToTable("Perros");
modelBuilder.Entity<Gato>().ToTable("Gatos");
modelBuilder.Entity<Perro>().Property(p => p.Id).ValueGeneratedNever();
modelBuilder.Entity<Gato>().Property(g => g.Id).ValueGeneratedNever();
}
¿Cuándo usar TPH, TPT o TPC?
Ideal cuando la jerarquía de herencia es simple y no hay muchas propiedades específicas de cada tipo. Es la opción más común debido a su simplicidad y rendimiento.
Ventajas de TPH
- Simplicidad: Solo se necesita una tabla para almacenar todas las entidades en la jerarquía.
- Rendimiento: Las consultas son más rápidas ya que no se requieren joins entre tablas.
Desventajas de TPH
- Espacio desperdiciado: Las columnas que no son aplicables a todas las entidades pueden contener valores nulos, lo que puede llevar a un uso ineficiente del espacio.
Recomendado cuando se necesita un esquema de base de datos normalizado y claro. Es útil en escenarios donde las propiedades específicas de cada tipo son significativas.
Ventajas de TPT
- Normalización: Cada tabla contiene solo las propiedades específicas de su tipo, lo que reduce la redundancia.
- Claridad del esquema: El esquema de la base de datos es más claro y fácil de entender.
Desventajas de TPT
- Rendimiento: Las consultas pueden ser más lentas debido a los joins entre tablas.
Adecuado cuando se necesita un rendimiento óptimo y no importa la redundancia de datos. Es un buen término medio entre las dos anteriores.
Ventajas de TPC
- Normalización parcial: Cada tabla contiene solo las propiedades específicas de su tipo, pero las propiedades de la clase base se duplican.
- Rendimiento: Las consultas pueden ser más rápidas que en TPT ya que no se requieren joins para acceder a las propiedades de la clase base.
Desventajas de TPC
- Redundancia: Las propiedades de la clase base se duplican en cada tabla.