martes, 1 de mayo de 2012

Generando entidades con Entity Framework

La oficina es un foco de conversaciones y discusiones (absurdas la mayoría de las veces). El otro día un compañero me comentaba que estaba encantado trabajando con EF. Y yo le pregunté por curiosidad "¿Y cómo generas las entidades?" y su respuesta fue "¿Cómo que cómo?... desde el menú". Mi compañero desconocía que hay varias formas de generar varias formas de generar entidades con EF.

Data Base First y Model First.
Estos son las dos formas soportadas por el IDE. Data Base first es la aproximación más utilizada por los usuarios, se modela la base de datos y se utiliza el asistente de entity framework dejando todas las opciones por defecto.


Model First es muy parecido, desde el asistente del Entity Data Model, seleccionamos Empty model creamos el modelo. El diseñador cuenta con un sasistente para generar el script de la base de datos.

Plain Old CLR Objects (POCOs)
Una de las características más criticadas de la primera versión de EF fue que las entidades generadas derivaban de la clase EntityObject.
Las entidades de se generan mediante plantillas t4, estas plantillas se pueden cambiar o modificar. De esta manera, si nos interesa crear POCOs tan solo debemos indicarlo añadiendo una plantilla al proyecto.

Code First.
Desde la aparición del EF 4.1 existe una nueva forma de definir el mapeo de las entidades vía código. A esto los amigos de Redmond le han llamado CodeFirst.
Por defecto todo funciona con una convención predefinida.
Pero si queremos trabajar personalizando los nombres o con una base de datos legacy, CodeFirst nos permite definir los mapeos mediante dos formas, vía atributos o mediante código directo.
Supongamos este Modelo de datos:
Lo único que tenemos que hacer es crear las clases que necesitemos. En este caso para personalizar el nombre de la tabla o indicar la PK utilizaremos anotaciones
    [Table("Libros")]
    public class Libro
    {
        [Key]
        [Column ("IdLibro")]
        public int Id { get; set; }
        public string Titulo { get; set; }
        public ICollection<Autor> Autores { get; set; }

        public Libro()
        {
            Autores = new HashSet<Autor>();
        }
    }

    [Table ("Autores")]
    public class Autor
    {
        [Key]
        [Column("IdAutor")]
        public int Id { get; set; }
        public string Nombre { get; set; }
        public ICollection<libro> LibrosEscritos { get; set; }

        public Autor()
        {
            LibrosEscritos = new HashSet <libro>();
        }
    }
Ahora lo que debemos generar es el DataBaseContext para que EF sepa qué clases debe mapear

    public class LibrosContex : DbContext
    {
        public LibrosContex()
            : base("ConnectionStringName")
        {

        }
        public DbSet<Libro> Libros { get; set; }
        public DbSet<Autor> Autores { get; set; }
    }

Desgraciadamente, a fecha de hoy la personalización mediante atributos de EF no permite personalizar el nombre de la tabla intermedia. Esto me permite completar el ejemplo utilizando el API fluido para la configuración del mapeo, indicando el nombre de la tabla intermedia en la relación Many to Many.
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Libro>()
                .HasMany<Autor>(l => l.Autores)
                .WithMany(a => a.LibrosEscritos)
                .Map(t =>
                {
                    t.MapLeftKey("IdAutor");
                    t.MapRightKey("IdLibro");
                    t.ToTable("AutoresLibros");
                });
            
        }

El uso del contexto generado con code first es igual que el generado por los asistentes
static void Main(string[] args)
        {
            using (var dbContex = new LibrosContex())
            {
                var libro = new Libro();
                libro.Titulo = "El Hobbit";

                var autor = new Autor();
                autor.Nombre = "J.R.R. Tolkien";

                libro.Autores.Add(autor);
                autor.LibrosEscritos.Add(libro);

                dbContex.Autores.Add(autor);

                dbContex.SaveChanges();

                var n = dbContex.Libros.Count<Libro>().ToString();
                Console.WriteLine("Libros en BD: " + n);
                Console.ReadLine();
            }
        }


Resumiendo
Entity Framework nos permite elegir varias estrategias de generación de las entidades y los mapeos: Data Base First, Model First y Code First. Además de poder elegir si las entidades son POCOs o no.
Personalmente prefiero code first, aunque sea por la sensación de control o de menos magia que el resto de formas.

1 comentario: