lunes, 14 de noviembre de 2011

Herencia en Entity Framework

A propósito de la lectura del libro ADO.NET Entity Framework 4.0 de campusmvp, me han parecido interesantes las distintas opciones que se nos presentan a la hora de modelar la herencia en un modelo relacional (ER) y su representación en Entity Framework.

Lo cierto es que con la herencia en un modelo relacional pasa como con los patrones de diseño, esto es que aunque no hayas estudiado previamente las distintas soluciones, seguro que ya estás utilizando alguna de ellas en muchos de tus proyectos.

Para persistir la herencia en base de datos y además poder trabajar después con ella después en un modelo de dominio con Entity Framework, tenemos disponibles las siguientes soluciones:

  • Herencia por jerarquía (TPH Table Per Hierarchy)
  • Herencia por tipo (TPT, Table Per Type)

Yo personalmente utilizo casi siempre la primera opción, pero después de escribir este post espero poder elegir más científicamente cual de ambas aproximaciones me encaja más por cada grafo de herencia. Ahora ya lo tengo claro y no son formas excluyentes, cada una tiene sus ventajas y desventajas.

Cómo el movimiento se demuestra andando, vamos a ver cómo implementar una herencia simple con ambas soluciones.

Nuestra herencia será la siguiente desde el punto de vista conceptual (expresada en un diagrama de clases UML):

image

El problema es ahora la impedancia que existe entre el modelo del dominio y el modelo relacional, o lo que es lo mismo ¿Cómo guardar esto en una base de datos?

Si optamos por la “Herencia por jerarquía” (TPH), debemos crear la siguiente estructura en base de datos:

clip_image002[4]

Si nos fijamos, la tabla contiene:

  • Los campos de la entidad Persona (Nombre y Apellidos).
    • Podrían o no aceptar nulos.
  • Los campos de la entidad Cliente (Direccion)
    • Tiene que aceptar nulos.
  • Los campos de la entidad Vendedor (CodigoInterno).
    • Tiene que aceptar nulos.
  • Un campo discriminatorio que delimita si el registro el del tipo Cliente o Vendedor (TipoPersona)
    • Puede aceptar nulos, porque además de un Cliente y un Vendedor, podríamos guardar una Persona.

Lo más relevante de la solución TPH es que toda la jerarquía de herencia se guarda en una sola tabla, que es la suma de las propiedades de la clase base y las propiedades de las clases hijas o derivadas. Además, es obligatorio incluir un campo discriminatorio para saber a qué tipo de entidad pertenece el registro activo.

Las desventajas de TPH son:

  • Como un registro guarda entidades de varios tipos, todos los campos que no pertenezcan al tipo de entidad activa tienen que ser nulos y siendo así, no podemos garantizar la coherencia del tipo de entidad activa puesto que no podemos, por ejemplo, jugar con la restricción de aceptar o no nulos en determinados campos de nuestra entidad puesto que todos los campos de la tabla deben de admitir nulos (excepto los campos de la entidad base y el campo discriminatorio).
  • Además, si hay muchos elementos en la jerarquía o las entidades tienen muchas propiedades, la tabla se puede convertir en un gran monstruo en lo relativo a su tamaño y gestión.

Para mapear la tabla Personas en nuestro modelo de Entity Framework, tendremos que utilizar una condición en el mapeo de las 3 entidades.

clip_image003[4]

  • Persona – When TipoPersona es NULL
  • Cliente – When TipoPersona es C
  • Vendedor – When TipoPersona es V

Habiendo visto ya la primera de las soluciones (TPH), veamos ahora este mismo ejemplo utilizando “Herencia por tipo” (TPT).

En la herencia por tipo, en el modelo relacional no tenemos una sola tabla sino que tenemos una tabla para los datos comunes (propiedades de la clase base) y una tabla por cada clase derivada en la jerarquía con sus propiedades concretas.

El modelo relacional sería el siguiente:

clip_image004[4]

Recuerda que en las foreign key entre clases bases y clases derivadas, la tabla principal tiene que ser la clase base y las tablas externas las clases derivadas. Es decir, la relación es desde Personas a Vendedores.

El modelo de dominio propuesto por Entity Framework si utilizamos Database First, será el siguiente:

clip_image005[4]

Siendo así, tendremos que realizar las siguientes operaciones:

  • Eliminar las asociaciones del modelo
  • Crear la herencia entre clases derivadas y la clase base
  • Eliminar las propiedades de clave de las clases derivadas. En nuestro caso, eliminaremos IdCliente de la entidad Clientes e IdVendedor de la entidad Vendedores.
  • Mapear el campo IdCliente de la tabla Clientes al campo IdPersona de la tabla Personas.
  • Mapear el campo IdVendedor de la tabla Vendedores al campo IdPersona de la tabla Personas.

clip_image007[4]

Finalmente, con cualquier de ambas soluciones (TPH o TPT) llegamos al mismo modelo de Entity Framework.

clip_image008[4]

Para terminar, espero que este post te haya dado una idea de cómo implementar la herencia en un modelo relacional y sobre todo, en un modelo de dominio con Entity Framework con el enfoque Database First (si estás usando Code First visita este enlace donde se explica perfectamente y además se introduce un tercer tipo de herencia denominado TPC – Table per Concrete Type).

Un saludo!

1 comentario:

  1. hola sergio buen blog, me sirvió para algo que se está desarrollando ya que se tiene algo así como medico, paciente, auxiliar enfermería, estoy tratando de modificar si estos derivan de clase persona o sea un TPH ya que están como tablas separadas, el rendimiento en cuanto a consultas (linq) o cualquier otra consulta(sql) se verían desmejoradas???

    ResponderEliminar