jueves, 20 de octubre de 2011

Introducción a Entity Framework

Si sigues el blog, sabrás que en mi empresa estamos haciendo un gran esfuerzo para incluir nuevas tecnologías en nuestros desarrollos, que nos hagan ser más productivos y competitivos en el mercado.

Una de ellas es ADO.NET Entity Framework. EF (como conoceremos de ahora en adelante a Entity Framework) es un ORM, pero ¿Qué aportará exactamente un ORM a nuestros desarrollos? Y después de resolver esta pregunta ¿Cuáles son los principales componentes que forman EF? Es decir, este post pretende sentar las bases de EF y servir como impulso para la publicación de futuros post mucho más técnicos.

Para la redacción de este post, reconozco haber plagiado (perdón, quería decir adaptado), muchos de los contenidos del maravilloso libro ADO.NET Entity Framework 4.0, que te recomiendo adquieras con urgencia porque es toda una joya al respecto.


Volviendo al tema, una de las grandes ventajas que aporta un ORM es la resolución del denominado desajuste de impedancia (o impedance mismatch en inglés).

El desajuste de impedancia hace mención a las diferencias existentes entre los modelos relacionales (base de datos) y los modelos conceptuales implementados en lenguajes de programación orientados a objetos.

Si reflexionamos sobre ello, la estructura física de una base de datos habla sobre tablas, filas, columnas, etc. mientras que los elementos disponibles en un lenguaje orientado a objetos son radicalmente distintos (clases, propiedades, herencia, interfaces, etc.). Es decir, estamos programando en dos mundos que nada tienen que ver el uno con el otro.

Cierto es que podríamos pensar en una tabla como en una clase, y en un campo como una propiedad, pero te prometo que la relación 1:1 entre tabla y clase no siempre es la mejor de la soluciones y de hecho muchas veces no es viable.

Por ejemplo, piensa en algunos ejemplos que podrían ilustrar que la relación 1:1 es una gran mentira:

En un primer ejemplo tenemos que guardar información de productos. Esta información se guarda en base de datos en 2 tablas (Productos y EstadosDeProducto), pero sin embargo, en nuestro modelo conceptual (nuestro código: donde nos sentimos cómodos y pasamos la mayor parte del tiempo), queremos trabajar con una clase Producto que unifica los datos de las dos anteriores tablas en una clase de entidad. Es decir, yo no quiero hablar de EstadosDeProducto en mi código, yo quiero hablar de un sola clase Producto con colección de estados.

En nuestro segundo ejemplo, en base de datos tenemos que guardar tanto Coches de época como Coches deportivos. Siendo así crearemos una tabla Coches con un campo Tipo, con valores E para época y D para deportivo (está claro que hay otras muchas soluciones para guardar este información, pero para nuestro ejemplo optamos por esta). Sin embargo, en mi código yo quiero tener una clase CocheEpoca y otra clase CocheDeportivo… y aún más, quiero que ambas hereden de una clase Coche. Como podemos observar, nada tiene que ver todo esto con cómo hemos guardado los distintos tipos de coche y como queremos trabajar con ellos en nuestro código. Esto es lo que llamamos el desajuste de impedancia y por ello un ORM nos va a ayudar a salvar este problema pudiendo modelar en nuestro código (modelo conceptual) las clases que necesitemos a partir de nuestro (y radicalmente distinto) modelo relacional.

Lo cierto es que el desajuste de impedancias da para un post entero, pero aquí simplemente quería recalcar lo importante de este concepto y cómo EF nos ayudará a resolverlo.

Ahora que ya sabemos el porqué necesitamos un ORM, tengo que reconocer que hasta día de hoy todos mis desarrollos han sido guiados por la base de datos y no por un modelo conceptual o modelo de dominio, que representa mejor la abstracción del mundo real para el que se está desarrollando el programa. Es decir, yo modelo la realidad del problema en la base de datos y después comienza una dura y encarnizada batalla en el código para intentar sobreponerme a las diferencias entre el modelo relacional y el modelo conceptual.

Como ya hemos adelanto, EF nos ayudará a salvar el problema del desajuste de impedancia a través de un modelo conceptual de entidades que se superpone por encima de la base de datos y nos hace olvidar la estructura física donde persiste el modelo.

Por ejemplo, mi modelo conceptual podría ser radicalmente distinto a mi modelo relacional y además podría incluso ser guardado en distintos orígenes de datos sin que yo tuviera que tocar el código de mi aplicación (esto gracias a que el modelo de dominio es ignorante respecto a la persistencia, simplemente utiliza repositorios con contratos bien definidos para grabar y recuperar las entidades del modelo).

Incluso dando una vuelta de tuerca al concepto, podemos diseñar directamente en EF nuestro modelo de dominio y después delegar en EF para que genere, como él crea oportuno, la estructura de tablas en una base de datos. Esto es lo que llamamos Model First (aquí incluso hay una tercera vía llamada Code First que queda fuera del ámbito de este post).

Con EF directamente le estamos dando a la base de datos la importancia que tiene, que es sólo persistir el estado de nuestro modelo. Nada más y nada menos. Nos da igual como realice esta tarea y no es nuestra intención bucear en las tripas de la base de datos, sino sólo consumir entidades a través del modelo de dominio.

Retornando al propósito inicial de este post, ahora veremos a vista de pájaro los principales componentes que conforman EF.

Componentes de EF

 

LINQ to Entities

Entity Client /

Entity SQL

Object Services

EDM (SSDL, MSL, CSDL)

Proveedores de EF

 

Proveedores de EF

EF es un ORM agnóstico de la base de datos contra la que trabaja. Es decir, nuestro mismo modelo de entidades debería funcionar igualmente con independencia de la base de datos que utilicemos para su persistencia (la teoría es que podríamos migrar una aplicación a cualquier otra base de datos y todo debería funcionar correctamente y, aunque con matices, esto es casi verdad).

La relación (o mapeo) entre las entidades de nuestro modelo conceptual y el esquema físico subyacente es gestionado por el proveedor de EF, porque es el proveedor quien conoce los detalles de cómo funciona internamente el motor de base de datos. Por ejemplo, en SQL Server un tipo Int32 se mapeará al tipo de base de datos int, mientras que en MySql podría ser distinto.

Entre estos proveedores podemos encontrar SQL Server, SQL Server Compact Edition, MySql, Oracle, SQLLite, PostgreSQL, etc. En realidad cualquiera puede escribir un proveedor de EF (ya sea una empresa o la comunidad).

Esto facilita la construcción de aplicaciones multi-base de datos, que es bueno tanto para empresas de desarrollo (ISV) que pretenden colocar su producto en el mercado, como para grandes empresas donde sus desarrollos internos no pueden estar ligados a una base de datos en concreto, ya que podría cambiar en el futuro por decisiones técnicas o incluso económicas. Es decir, estamos desacoplando el dominio del repositorio.

EDM

EDM (Entity Data Model) es la definición del mapeo que creamos entre la base de datos y nuestro modelo conceptual o de entidades.

Para construir y trabajar con nuestro EDM tenemos disponible en VS el diseñador  de modelos (Entity Data Model Designer).

Un modelo se guarda en un fichero con extensión edmx, que a través del lenguaje XML declara 3 secciones (que aunque puede estar en ficheros distintos, la norma es que estén en un solo fichero).

  • SSDL (Storage Schema Definition Language)
    • Describe la base de datos (tablas, vistas, columnas, relaciones, procedimientos almacenados, etc.)
  • CSDL (Conceptual Schema Definition Language)
    • Describe las entidades del modelo y la navegación entre las mismas.
  • MSL (Mapping Schema Language)
    • Define como se relaciona la sección SSDL con la sección CSDL.
    • Esta sección también es conocida como C-S.

Entity Client

Antes de comenzar te diré que yo no voy a utilizarlo porque utilizaré LINQ to Entities, pero aun así hay que saber lo que es para no poner cara de otro cuando se hable sobre ello.

Entity Client es un nuevo proveedor de ADO.NET (similar a SqlClient u OracleClient) pero que en vez de trabajar con la base de datos específica (como haría cualquier otro proveedor de ADO.NET, por ejemplo SqlClient con SQL Server, OracleClient con Oracle, etc.), trabaja sobre el modelo de entidades. De este modo, este proveedor se aprovecha del agnosticismo de EF y con una sola sintaxis podemos escribir consultas que no dependen del dialecto de la base de datos subyacente porque ataca directamente al modelo.

Cabe mencionar que serán después los distintos proveedores de EF quienes traduzcan nuestra consulta en Entity Client al dialecto de la base de datos específica.

Entity Client utiliza un lenguaje específico (más lenguajes, por dios…) para consultar el modelo de entidades, que se llama Entity SQL o eSQL.

eSQL pretende ser un dialecto parecido al T-SQL de toda la vida, pero mejorando ciertos aspectos relacionados con el modelo, como la navegabilidad, los tipos, etc.

No conozco mucha gente que trabaje con EF, pero lo que percibo por Internet, foros, etc. es que Entity Client no es muy utilizado.

Yo creo que esto será porque:

  • Es un nuevo lenguaje y además es específico de EF (es decir, fuera de EF no vale para nada).
  • No soluciona que tengamos que escribir la consulta en texto y entonces estemos con los errores de toda la vida en tiempo de ejecución, además de que el compilador no puede ofrecernos ayuda durante el tiempo de desarrollo (no hay Intellisense).

Object Services

Ya sea utilizando eSQL o LINQ to Entities, entre bastidores está claro que el proveedor de EF de turno traducirá nuestra consulta al lenguaje específico de la base de datos y devolverá los resultados en formato tabular, el formato de toda la vida, esto es filas-columnas y ¿Cómo podemos ahora transformar esos resultados devueltos por la consulta SQL en entidades del modelo? ¿Cómo pasar de un resultado con n filas y con columnas IdCliente, Nombre … a entidades Cliente?

Este proceso se conoce como materialización. La materialización es la transformación de los resultados obtenidos a través del proveedor de EF en objetos de las distintas clases que existen en nuestro modelo. Esta materialización es automática y además nos permite tener Intellisense y trabajar con datos fuertemente tipados .

LINQ to Entities o L2E

Es el proveedor de LINQ para EF.

L2E está construido sobre Object Services, por lo que funcionará perfectamente la materialización.

Con todo lo expuesto aquí está claro que no te vas a poner a programar con EF mañana mismo, pero al menos espero te sirva para poner en orden ciertos conceptos básicos sobre EF y sobre todo, sentir la necesidad de integrar un ORM en tus proyectos.

Un saludo!

8 comentarios:

  1. fuese bueno que introduzcas ejemplos resueltos no por mi persona, si no por los novatos, la teoria esta bien

    ResponderEliminar
  2. Anónimo, lo cierto es que este post es sólo teórico, pero si accedes al tag "Entity Framework" http://panicoenlaxbox.blogspot.com.es/search/label/Entity%20Framework hay algunos ejemplos que creo te resultarán útiles.
    En cualquier caso, reitero que es "compra obligada" el libro de Entity Framework de campusmvp, es una auténtica joya. Te dejo en enlace http://www.campusmvp.com/catalogo/Product-ADO.NET-Entity-Framework-4.1---Aplicaciones-y-servicios-centrados-en-datos_110.aspx
    Un saludo y gracias por comentar.

    ResponderEliminar
  3. Gracias por la informacion. Podrias publicar algo sobre Code First y lo nuevo q traera la version 5 del EF.

    ResponderEliminar
  4. Con Code First no estoy trabajando todavía, pero sobre información sobre EF, si te pasas por mi twitter, intento twittear todo la información relevante que encuentro. Un saludo.

    ResponderEliminar
  5. Me pareció una buena introducción al tema. La verdad es que si no se tiene cierta experiencia es difícil ver toda la foto del framework y entender claramente cada parte.
    Al respecto un par de comentarios: hay una diferencia conceptual entre el lenguaje Entity SQL y el proveedor Entity Client (no son lo mismo). Por otra parte los Object Services proveen una gran variedad de funcionalidad (control de cambios, concurrencia, transacciones, object identity, etc) dónde la materialización es solo una pequeña -pero importante- parte.
    ¡Saludos!

    ResponderEliminar
  6. Estoy trabajando en un proyecto, con EF6 con una capa de servicio WCF, actualmente el modelo actúa sobre una BD SQLSERVER , pero también ahora me toca trabajar en paralelo con una BD Oracle, como podría lograr eso sin tener que crear otra capa de servicio WCF? osea quiero q mi EF sea agnóstico :'( pero actualmente cuando mapeo el modelo contra la BD oracle me da problemas porque son diferentes tipos de datos ejem: SQLservr existe el bit pero en Oracle no.
    Ayuda xfavor dándome una guía de como hacer esto gracias :)

    ResponderEliminar
    Respuestas
    1. Una solución rápida es generar un nuevo archivo SSDL para Oracle y estar cambiando el nombre en la cadena de conexión. Espero te sea útil.

      Eliminar
  7. Busco ayuda por problema con Entity Framework: Tengo una Web Api con Entity Framework. La tabla de sql server utilizada se llama PERSONAL y tiene definido un campo llamado CANAL de tipo string not null con default 'tv'. Cuando se genera el modelo, el default se pierde, es decir, el modelo no agrega ese atributo.
    En la clase partial PERSONAL generada, no quiero agregar el default, ya que en una futura actualizacion de la clase, voy a perder lo modificado. Entiendo que debo generar una clase PERSONAL que complemente la clase principal, y en la misma redefinir el campo CANAL indicando su default.
    Quisiera saber si es correcto resolver el agregado del default con el agregado de la clase parcial personalizada?
    No se como se redefine un campo en una clase parcial para indicar que tiene un default ?.
    La clase generada por el modelo es:
    Partial Public Class PERSONAL
    Public Property CANAL As String
    End Class
    La clase que agregue para asignar el default es:
    Public Class PERSONAL
    NO SE COMO asignar a CANAL que tiene un default 'tv'
    End Class
    Espero por favor puedan ayudarme
    Muchas gracias

    ResponderEliminar