Juanma Santoyo

En ocasiones me llaman friki

Introducción a Entity framework Code First

| 3 Comentarios

En general, para usar Entity Framework; debemos tomar como punto de partida una base de datos existente. Por lo tanto, sin base de datos no podemos crear el modelo de persistencia.

Pero todo eso ha cambiado ya que desde hace unos pocos meses disponemos de Entity framework Code First. EFCF nos permite crear el modelo de base de datos tomando como punto de partida una serie de clases. Estas clases, serán nuestro modelo de persistencia, y también serán el punto de partida desde el cual EFCF creará el esquema de la base de datos.

Convención sobre configuración

Una de las grandes cosas de EFCF es que sigue una pauta de “Convención sobre configuración”. Esto es, que nosotros no debemos configurar todas las cosas que programamos en nuestro modelo de persistencia, si no que EFCF lo hará por nosotros siempre que sigamos las convenciones de EFCF. Por lo tanto, cuando programemos nuestro modelo de persistencia deberemos conocer y aplicar dichas convenciones.

Las convenciones de las que hablo en realidad son muy pocas y muy coherentes. Ahora puede parecer que son un inconveniente por tener que conocerlas todas, pero nada más lejos de la realidad: veremos cómo gracias a esta política el desarrollo de un modelo de persistencia se vuelve un proceso simple y muy rápido.

Por supuesto, todas las convenciones son configurables. Si quieres complicarte la vida, tú mismo con tu mecanismo…

Por ejemplo, supongamos que estamos creando la clase de persistencia para la tabla Producto. Lo primero que deberíamos hacer, es crear un campo que sea la clave primaria. ¿Cómo determinamos cuál es ese campo?. Fácil: Por convención, la clave primaria será aquel campo numérico llamado Id. Cabe destacar que nada en EFCF es case-sensitive (aunque el modelo de persistencia sí lo es).

Ejemplo práctico

Vamos a hacer un pequeño ejemplo para ilustrar el funcionamiento de EFCF. Crearemos un pequeño modelo de persistencia para dos tablas: Producto y Categoría.

Ya podemos imaginar el resto de la historia: ambas tablas tendrán un identificador y un nombre. Además, la tabla producto estará relacionada con la tabla categoría; de forma que un producto tenga una categoría asociada y desde una categoría se pueda acceder a la colección de todos los productos asociados.

Instalando Entity framework Code First

Deberemos descargar e instalar EFCF para poder dotar a Visual Studio de las nuevas características. Descarga Entity Framework Code First aquí.

El orden de instalación es:

  1. EF_JUNE_2011_CTP.msi
  2. DS_JUNE_2011_CTP.msi
  3. EFTools.msi

Las entidades

Crearemos un proyecto de tipo Librería de clases donde programaremos las clases que representarán las entidades. Atención aquí: debemos crear el proyecto bajo el framework Microsoft Entity Framework June 2011 CTP. El proyecto se llamará EFCFTest.Entities.

crear entities

Crearemos una clase llamada Categoria.cs. En esta clase crearemos dos campos: uno será el identificador y el otro el nombre de la categoría.

namespace EFCFTest.Entities
{
    public class Categoria
    {
        public long Id {get; set;}
        public string Nombre { get; set; }
    }
}

Como veis, el campo Id es numérico. Un campo que se llame Id y sea numérico se convierte por convención en la clave primaria de la entidad. Además, es autoincrementable.

Vamos a crear ahora la clase Producto.cs.

namespace EFCFTest.Entities
{
    public class Producto
    {
        public long Id { get; set; }
        public string Nombre { get; set; }
        public int Stock { get; set; }
    }
}

Esta clase se parece mucho a la anterior, pero le he añadido un campo de Stock.

Solo nos queda un pequeño pero importantísimo detalle: Tenemos que programar la relación entre las entidades. Lo lógico aquí es que un producto se clasifique en una categoría y que una categoría tenga asociada una colección de productos. Pues no se hable más, vamos allá:

Empecemos por la clase Producto. Por convención, para asociar una entidad Categoría a la clase producto lo haremos añadiendo estos campos a la clase Producto.

public long CategoriaId { get; set; }
public virtual Categoria Categoria { get; set; }

Voy a explicar primero la segunda de estas dos propiedades: cumple dos características muy importantes:

  • Es del tipo Categoria. Esto es así porque su valor será la entidad categoría con en la que está clasificado el producto.
  • Es virtual. Esto es importante, ya que determina que es una propiedad de navegación.

La primera propiedad también cumple dos características:

  • Es del mismo tipo que la clave primaria de Categoria. Esto tiene que ser así porque almacenará dicho valor.
  • El nombre de la propiedad es la concatenación de el nombre de la propiedad de navegación Categoria y el nombre de la clave primaria en la entidad Categoria, que es Id.

Ahora vamos a crear la relación desde la clase Categoria. Aquí lo que queremos es una colección de productos, por lo que añadiremos la siguiente propiedad:

public virtual ICollection<Producto> Productos { get; set; }

Los aspectos a destacar en esta propiedad son:

  • Al igual que la propiedad Categoria de la entidad Producto, es una propiedad de navegación y por lo tanto debe ser virtual.
  • Lógicamente, el tipo de la colección es Producto.

Las colecciones merecen un tratamiento un poco especial, ya que por defecto las colecciones son referencias vacías. Podemos modificar el objeto para que las colecciones vengan siempre inicializadas. Es sencillo, sólo deberemos añadir un constructor en nuestra clase. Realmente, en el constructor podríamos inicializar cualquier valor que queramos:

public Categoria()
{
	Productos = new List();
}

Con esto ya hemos acabado con las entidades. Como veis, el principio de convención sobre configuración nos obliga a hacer las cosas de una determinada manera, pero las convenciones son sencillas de aprender y nos ahorrarán mucho tiempo.

El contexto.

El contexto es el entorno que nos permitirá trabajar con las Entidades. Se trata de un objeto que usaremos para acceder a la capa de persistencia.

Vamos a crear un nuevo proyecto tipo Librería de clases donde programaremos el contexto de nuestra aplicación. Al igual que el anterior, este proyecto debe crearse bajo el framework Microsoft Entity Framework June 2011 CTP.

crear context

Este nuevo proyecto, extenderá de clases propias de EFCF, por lo que deberemos añadir una referencia a su DLL. Si has instalado EFCF tal y como indica este artículo, deberías encontrar la DLL en C:Program FilesReference AssembliesMicrosoftFramework.NETFrameworkv4.2System.Data.Entity.dll. Esto no tiene porqué ser así en tu entorno, ya que hay otras formas de instalar EFCF. También habrá que añadir una referencia al proyecto EFCFTest.Entities para poder hacer uso de las entidades que hemos creado previamente.

De momento, sólo vamos a añadir una clase. Quizás parezca absurdo crear un proyecto que va a contener una única clase, pero es más una cuestión de concepto y de escalabilidad: El contexto y las entidades no son exactamente lo mismo, por lo que no deberían estar juntos. El día de mañana, podríamos necesitar más contextos, o quizás necesitemos añadir lógica adicional a nuestro contexto.

La clase se llamará EFCFTestContext. Este nombre es importante, ya que por convención, es el nombre que recibirá el esquema de la base de datos que cree el contexto (el contexto es el encargado de crear la base de datos si no existe).

La clase es sencilla, sólo necesitará el siguiente código:

using System.Data.Entity;
using VoyAlCine.Entities;
namespace EFCFTest.Context
{
    public class EFCFTestContext : DbContext
    {
        public DbSet<Categoria> Categorias { get; set; }
        public DbSet<Producto> Productos { get; set; }
    }
}

Como se puede ver, también he indicado los using que vamos a necesitar para importar las referencias que acabamos de añadir.

Las cosas a destacar de éste código son:

  • La clase contexto hereda de DbContext. Esta es una clase de la DLL de EFCF.
  • Crearemos un DbSet por cada entidad que hayamos creado. La clase DbSet también es parte de EFCF.

Sólo queda una cosa por decir sobre la clase contexto: sería lógico querer cambiar el nombre del esquema de la base de datos. Esto es sencillo, solo debemos añadir un constructor que sobre escriba la llamada al constructor de DbContext. Como parámetro, le pasaremos el nombre que deseamos darle a la base de datos:

public EFCFTestContext() : base("EFCFTest")
{
}

Creamos una aplicación de prueba

Por último, vamos a crear una aplicación que nos sirva para realizar algunas pruebas sobre nuestro nuevo modelo de persistencia.

Vamos a crear una aplicación de consola llamada EFCFTest.ConsoleApp. No hay que olvidar que debemos seleccionar el framework Microsoft Entity Framework June 2011 CTP.

Lo primero que haremos será agregar a las referencias los proyectos de entidades y contexto. También debemos añadir una referencia a la DLL System.Data.Entity.dll, tal y como hicimos en el proyecto de contexto.

crear consoleapp

Otra cosa importante que debemos hacer, es añadir la cadena de conexión a la base de datos. Esta cadena de conexión se debe añadir al app.config del proyecto, en el nodo configuration/connectionStrings. La cadena de conexión deberá ser algo como:

Data Source=<Servidor de la base de datos>;Initial catalog=<em>EFCF</em>Test;Integrated Security=True;MultipleActiveResultSets=True

Atención al parámetro Data Source. Este debe ser el servidor donde esté la base de datos. He puesto las marcas < y > por legibilidad, pero debéis quitarlas cuando pongáis el servidor.

Al final, vuestro app.config debe quedar más o menos así:

<configuration>
  <!-- … -->
  <connectionStrings>
    <add name="VoyAlCineContext"
         connectionString="Data Source=<Servidor de la base de datos>;Initial catalog=<em>EFCF</em>Test;Integrated Security=True;MultipleActiveResultSets=True"
         providerName="System.Data.SqlClient"/>
  </connectionStrings>
</configuration>

El parámetro providerName es muy importante.

Crear registros

Vamos a crear algunos registros. Lo primero que debemos hacer es crear una instancia del contexto al cual queramos acceder:

EFCFTestContext context = new <em>EFCF</em>TestContext();

Vamos a crear una categoría y le vamos a asociar dos productos:

Categoria Quesos = new Categoria();
Quesos.Nombre = "Quesos";

Quesos.Productos.Add(new Producto()
{
  Nombre = "Queso Edam"
  , Stock = 10
});

Quesos.Productos.Add(new Producto()
{
  Nombre = "Queso Havarti"
  , Stock = 5
});

context.Categorias.Add(quesos);

context.SaveChanges();

Lo que he hecho es lo siguiente: He creado un objeto Categoria, le he inicializado la propiedad Nombre, y después le he incluido en la colección de productos dos objetos Producto. Por último, lo más importante: he añadido la categoría al contexto, y he persistido los cambios con el método SaveChanges. Este último paso es esencial, ya que es lo que guarda en base de datos todos los objetos que hemos añadido al contexto.

Para tener un conjunto de datos más decente, voy a crear un par de categorías más, con sus respectivos productos. Haré algunas modificaciones sobre el código anterior que no voy a reflejar en este artículo por cuestiones de legibilidad. Como siempre, al final encontraréis una versión descargable de este ejemplo donde podréis consultar todo el código fuente.

Si ahora ejecutamos nuestra aplicación de consola, veremos cómo se crea nuestra base de datos y se inicializa con unos cuantos datos. Vamos a añadir un poco de código más al programa. Este código sólo servirá para monitorizar lo que está ocurriendo, y para evitar que la aplicación se cierre automáticamente (todas las aplicaciones de consola se cierran al acabar, a no ser que nosotros programemos que no sea así).

El código que añadiremos será:

  1. Después de añadir una categoría al contexto añadiremos ésta línea:
    Console.WriteLine("Se ha creado la categoría {0}", Quesos.Nombre);
  2. Al final de nuestro programa, forzaremos que la aplicación espere la introducción de la tecla enter, de forma que no se cerrará hasta que se pulse enter:
    Console.WriteLine("Operación finalizada. Pulsa ENTER para salir...");
    Console.ReadLine();

Por último, antes de ejecutar la aplicación. Hay que tener en cuenta que se deba establecer la aplicación de consola como el proyecto de inicio:

establecer como proyecto de inicio

 

Lo que ocurra en nuestra pantalla debería parecerse a esto:

resultados creacion

Seguramente habréis notado un tiempo de inactividad excesivo al ejecutar la aplicación por primera vez. Esto se debe a que la base de datos no existía y se ha creado. Si accedéis a la base de datos con un gestor como el Microsoft SQL Management Studio o similar, podréis ver cómo se ha creado la base de datos:

bdd creada

Por supuesto, también se habrán creado los registros que hemos programado.

Seleccionar registros

Ahora vamos a ver cómo podemos seleccionar registros de la base de datos.

Lo primero que haremos será comentar el código anterior, ya que no queremos que se vuelvan a crear más registros. Sólo dejaremos sin comentar las líneas que crean el objeto Context y las que pausan la ejecución del programa. Comentaremos incluso la llamada al método SaveChanges, ya que no queremos modificar el contenido de la base de datos.

Hay varias maneras de acceder al contenido. Por ejemplo, desde el objeto Context podríamos obtener un listado con todas las categorías:

Console.WriteLine("nTodas las categorías:");
List<Categoria> Categorias = Context.Categorias.ToList<Categoria>();
foreach (Categoria Categoria in Categorias)
{
    Console.WriteLine("{0}t{1}", Categoria.Id, Categoria.Nombre);
}

Podríamos usar linq, personalmente es el método que más me gusta:

var ProductosQueso = from p in Context.Productos
where p.Nombre.ToLower().Contains("queso")
      select p;

Console.WriteLine("nProductos cuyo nombre contiene la subcadena "queso":");
foreach (Producto Producto in ProductosQueso)
{
    Console.WriteLine("t{0}.", Producto.Nombre);
}

Linq nos permitiría incluso hacer joins entre tablas:

ProductosQueso = from p in Context.Productos
join c in Context.Categorias on p.Categoria equals c
where c.Nombre.ToLower() == "Quesos"
select p;

Console.WriteLine("nProductos en la categoría "Quesos":");
foreach (Producto Producto in ProductosQueso)
{
    Console.WriteLine("t{0}.", Producto.Nombre);
}

También podríamos usar las lambda expressions de linq. Hacen el uso de linq más simple, pero no son tan potentes como usar linq directamente, y complican la lectura del código:

List<Producto> ProductosFrutosSecos = Context.Productos.Where(
p => p.Categoria == Context.Categorias.FirstOrDefault(c => c.Nombre.ToLower() == "frutos secos"))
.ToList<Producto>();

Console.WriteLine("nProductos en la categoría "Frutos secos":");
foreach (Producto Producto in ProductosFrutosSecos)
{
    Console.WriteLine("t{0}.", Producto.Nombre);
}

Por último, podríamos acceder a los elementos según el valor de su clave primaria. Es el método más simple de todos ya que permite seleccionar contenidos sin el uso de linq, pero sólo podríamos usarlo sabiendo la clave primaria del elemento que necesitamos, y no permite seleccionar conjuntos (solo elementos aislados):

Categoria CategoriaRefrescos = Context.Categorias.Find(2);
Console.WriteLine("nProductos en la categoría "Refrescos":");
foreach (Producto Producto in CategoriaRefrescos.Productos)
{
    Console.WriteLine("t{0}.", Producto.Nombre);
}

resultados listado


Borrar registros

Borrar los registros existentes no puede ser más sencillo:

Categoria CategoriaFrutosSecos = (from c in Context.Categorias
    where c.Nombre.ToLower() == "frutos secos"
    select c).First<Categoria>();

Context.Categorias.Remove(CategoriaFrutosSecos);

Console.WriteLine("nSe ha borrado la categoría {0}", (CategoriaFrutosSecos).Nombre);

Cómo veis, tan solo hay que localizar el registro a borrar y usar el método Remove.

Descarga el ejemplo

Como intento hacer siempre, os dejo la descarga del ejemplo.

¡Hemos terminado!

En éste artículo hemos explicado cómo crear una base de datos y su modelo de persistencia con Entity framework Code First y también hemos añadido varios ejemplos de cómo usarla. Ha sido un artículo muy completo que espero que os resulte útil.

Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone

3 Comments

  1. Buen articulo!
    Una consulta EFCF ya viene incluido en vs2012?

    Gracias

  2. Probablemente sí. Aunque en caso contrario, las librerías necesarias se pueden descargar fácilmente con nuget.

    Saludos.

  3. Excelente, gracias.

Deja un comentario

Los campos obligatorios están marcados con *.