Entity Framework: Code First

En posts previos vimos dos alternativas para implementar Entity Framework en nuestras aplicaciones web ASP.NET MVC: Database First y Model First. En este post es el turno de la última opción que podemos elegir: Code First.

Code First se considera la alternativa más adecuada para aquellos casos en los que hay que crear la base de datos desde cero en conjunto con la aplicación, y es el equipo de desarrollo quien debe hacerlo. Usando Code First literalmente no deberemos escribir una sola sentencia SQL (lo cual no implica que no debamos conocer aspectos propios de su arquitectura). El enfoque es muy similar a lo que nos propone Model First, pero en vez de trabajar con una herramienta de diseño lo haremos directamente desde el código. Además nos provee algunas opciones interesantes, que iremos viendo a lo largo de este post.

Lo primero que vamos a hacer es agregar EntityFramework a nuestro proyecto (si es que ya no está la referencia). Esto lo podemos hace por la consola de NuGet a través del siguiente comando:
Install-Package EntityFramework

Luego crearemos todas las clases correspondientes a los objetos del dominio de nuestra aplicación que debemos modelar para almacenar en la base de datos. Esto lo haremos creando las clases de forma normal con todas sus propiedades, salvo que debemos marcar las propiedades de tipo complejo como virtuales.

Para este caso vamos a usar el mismo ejemplo que usamos en los posts de Database First y Model First, el cual define las siguientes clases:

  • Books: almacena los libros.
  • Editorials: almacena las editoriales.
  • Authors: almacena los autores.
  • Withdraws: almacena los retiros que hacen los usuarios.
  • Users: usuarios que hacen los retiros.

Por ejemplo para la clase Book que representa un libro, el resultado es el siguiente:

CodeFirst - Clase Book
Clase Book

Como vemos, la definición de las propiedades es normal a cualquier otro caso que debemos realizar en nuestra aplicación, salvo la definición del tipo virtual antes mencionada.

Ahora debemos crear el DataContext asociado a nuestro acceso a datos. Para ello crearemos la clase CodeFirstContext:

CodeFirst - Nuevo Context
DataContext de la aplicación

Como se puede ver en la imagen la clase deberá heredar de DbContext. En este DataContext deberemos registrar todas las entidades que queremos que se creen en la base de datos, definiéndolas como un tipo DbSet. El nombre de la propiedad será el que tenga la tabla asociada.

Ahora agreguemos la cadena de conexión en el Web.config que querramos usar para la nueva base de datos. Cabe destacar que no debemos usar las generadas por EntityFramework cuando usamos el enfoque Database First (las cuales comienzan con la información de metadata), ya que con ellas el funcionamiento no será el adecuado. En este caso, usamos la siguiente cadena de conexión:

CodeFirst - ConnectionString
Cadena de conexión

En dicha cadena de conexión especificamos cual será el nombre que le daremos a la base de datos a crear. Ahora desde alguna parte de la aplicación debemos realizar algo contra el DataContext, de forma que se inicialice y cree la base de datos. En este ejemplo, en la acción Index del HomeController crearemos un nuevo autor:

CodeFirst - CrearNuevoObjeto
Nuevo objeto para inicializar

Ahora probemos ejecutar la aplicación y ver que pasa en nuestra base de datos. El resultado es el siguiente:

CodeFirst - ResultadoInicial
Resultado inicial en la base de datos

Como se puede observar, sin aplicar una sola sentencia SQL por nuestra parte tenemos creada la base de datos con las entidades definidas en nuestra aplicación. Para casos sencillos puede que nos sirva una definición de la base de datos “plana” como la que generamos en este caso, pero hay casos más complejos en los que necesitamos definir ciertas características. Para ello contamos con DataAnnotations y FluentAPI.

A través de DataAnnotations podremos definir por ejemplo la clave primaria de nuestra entidad, a través del atributo [Key]. También podemos definir el tipo de datos que queremos que se asocie a una determinada propiedad como por ejemplo [Column(TypeName = "nvarchar(250)")].

CodeFirst - DataAnnotations
Ejemplos de DataAnnotations

DataAnnotations se caracteriza por lograr que las clases queden mucho más prolijas, teniendo la información asociada al mapeo directamente en cada propiedad. Pero hay cosas que no podemos definir completamente utilizando DataAnnotations, por lo cual debemos recurrir a la segunda opción: FluentAPI.

Con FluentAPI las definiciones se realizan en clases específicas de mapeos, o en el método OnModelCreating del DataContext que estemos utilizando. En este caso vamos a usar la segunda opción, realizando la misma definición que realizamos con DataAnnotations en el ejemplo anterior.

CodeFirst - FluentAPI
Ejemplos de FluentAPI

Como decíamos anteriormente, la ventaja de usar FluentAPI es la potencia que tiene para definir diferentes parámetros asociados al mapeo. Aunque en este caso de ejemplo solo realizamos definiciones sobre tipos de datos, su mayor utilidad es cuando tenemos que definir algún tipo de relación entre diferentes objetos y sus tablas correspondientes en la base de datos (aunque no necesariamente deba haber una equivalencia del tipo uno-a-uno).
Volvamos a correr la aplicación con estos cambios. Veremos que el resultado es el siguiente:

CodeFirst - ErrorCambios
Error al ejecutar la aplicación con cambios

En este caso vemos que el error que nos da la aplicación es que se ha producido una diferencia entre lo definido en nuestra aplicación y lo establecido en la base de datos. La primera vez que ejecutamos la aplicación no hubo problemas, ya que al no existir estructuras previas se creó todo de cero. Aquí tenemos dos alternativas: o borramos toda la base de datos cuando se produce un cambio en las estructuras o utilizamos migraciones.

Las migraciones tal vez sean uno de los puntos fuertes de CodeFirst (sobre todo en relación a ModelFirst), ya que nos permiten ejecutar cualquier modificación que realicemos asociada a los mapeos definidos en el DataContext sobre la base de datos existente. Para habilitarlas, debemos ejecutar en la consola de NuGet el comando Enable-Migrations.

Al finalizar la ejecución tendremos creada la carpeta Migrations con la migración inicial de la BD (su estado actual) y una clase de configuración. En esta última podremos definir diversas configuraciones, como si las migraciones se realizarán de forma automática o no, o que sucede si la modificación a realizar involucra pérdida de datos existentes.

CodeFirst - InstalarMigrations
Carpeta creada al habilitar migraciones

Ahora debemos crear la migración correspondiente a la modificación que hicimos en los mapeos. Para ello ejecutamos en la consola de NuGet el comando Add-Migration Initial (donde Initial es el nombre que queremos darle a la migración en particular). El resultado es una clase donde se definen las modificaciones que EntityFramework debe realizar a la base de datos, y como deshacer los cambios si ocurre un error en dicha ejecución.

CodeFirst - MigracionInicial
Código generado en la migración

Para aplicar los cambios en la base de datos solo debemos ejecutar en la consola el comando Update-Database. El mismo ejecutará todas las migraciones pendientes sobre la base de datos, y nos notificará el resultado. Si queremos ver las sentencias SQL que ejecuta esta acción (como en la imagen a continuación) podremos agregar al comando el parámetro -Verbose.

CodeFirst - UpdateDatabase
Actualizar base de datos

Y listo, con esto ya tenemos nuestra aplicación funcionando nuevamente. De esta forma, solo tendremos que ir generando migraciones a medida que las clases de nuestra aplicación vayan cambiando, siendo una forma muy sencilla de lograr una alta mantenibilidad ante los cambios.

Como conclusiones, podemos decir que para los desarrolladores CodeFirst es una gran alternativa, ya que podemos definir todas las clases de dominio desde el código de nuestra aplicación, e incluso personalizar y definir ciertas situaciones particulares que requieran una resolución particular en la base de datos. La utilización de migraciones marca una diferencia muy importante con ModelFirst, ya que con esta última solo se nos generaba el código SQL de todo el modelo, sin tener en cuenta si ya había estructuras creadas y su estado.
Como desventaja, es un buen enfoque para comenzar con la creación de una aplicación y su base de datos asociada, pero su gestión comienza a dificultarse a medida que se avanza en el tamaño del proyecto, la cantidad de personas que trabajan en el mismo y los ambientes en los cuales deben ejecutarse las modificaciones. Esto debido a que no podemos realzar las modificaciones de forma manual cuando nosotros lo querramos, sino que lo hará EntityFramework a partir de la información de migraciones previas. Si por algún motivo se pierde la sincronización entre la base de datos y la información de migraciones que maneja EntityFramework, la situación se torna bastante complicada para resolver (más si estamos hablando de una aplicación en un ambiente productivo).
Generalmente (y en mi experiencia también fue así) se opta por esta alternativa para comenzar, pero a medida que se avanza se pasa al enfoque DatabaseFirst, en el cual se puede tener un mayor control sobre los cambios que se realizan en la base de datos y su correlación en la aplicación.

Con este post terminamos la serie dedicada a EntityFramework y sus alternativas de implementación. Espero que les haya sido útil, y que apliquen a sus proyectos aquellas que le sean adecuadas a cada caso. Cualquier consulta o comentario de cuales fueron sus experiencias en los distintos casos es bienvenida!

Anuncios

7 comentarios en “Entity Framework: Code First

  1. Buen día, muy interesante el post…actualmente tengo un libro de Entity Framework que comencé a leer hace muy poco. Si bien, he utilizado code first desde hace más de 6 meses atrás, hay cosas que a veces no me cuestiono mucho y simplemente las escribo por inercia.

    Claramente desde el punto de vista de nuestro rubro, es algo que no debería ocurrir, pero sin embargo tu post es bastante explicativo y entendí bastante, felicidades.

    Por cierto, yo igual comencé a escribir un blog, mas que nada a modo de vitácora personal…tomaré el tuyo como referencia para mis futuros aportes.

    Un saludo desde Chile

    Me gusta

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s