domingo, 2 de febrero de 2014

[EntityFramework] Configurando nuestras entidades de dominio con Fluent API

Cómo apreciamos en nuestro artículo anterior Configurando nuestras entidades de dominio con Data Annotations logramos configurar nuestras entidades de dominio a través de Data Annotations, con esto logramos definir características particulares de entidades y propiedades a la hora de ser mapeadas para la creación de la base de datos, esta es una forma de hacerlo, pero no la única que nos brinda Entity Framework Code First. En este artículo vamos a ver cómo podemos hacer lo mismo pero esta vez usaremos Fluent API, que nos permite hacer este tipo de configuraciones de una forma centralizada, teniendo acceso a cada entidad que tengamos en nuestro contexto y usando métodos encadenados y expresiones lambda para realizar la configuración.

Ahora vamos a implementar la misma configuración del ejemplo anterior dónde usamos Data Annotations, con Fluent API para que podamos comparar más fácil cómo es el trabajo con cada una, y en base a esto tomar una elección personal de cuál usar.

Lo primero que debemos hacer para usar Fluent Api, es sobre escribir el método OnModelCreating de nuestro contexto, ya con esto al interior de este método podremos realizar las configuraciones sobre las entidades, tal y cómo se muestra a continuación:

    public class Context : DbContext
    {
        public Context() : base("Productos")
        {
            
        }
 
        public DbSet<Producto> Productos { getset; }
 
        public DbSet<Categoria> Categorias { getset; }
 
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // Aquí haremos nuestras configuraciones con Fluent API.
            base.OnModelCreating(modelBuilder);
        }
    }

Cómo vemos ahora el método OnModelCreating recibe el parámetro modelBuilder de tipo DbModelBuilder, es con esta clase que lograremos hacer todas las configuraciones que necesitamos, ahora sí vamos a ver como lograr dichas configuraciones, las cuales haremos sobre la entidad Producto, la cual usamos en nuestro ejemplo anterior de Data Annotations:

    [Table("Productos")]
    public class Producto
    {
        [Key]
        public int Codigo { getset; }
 
        [Required]
        [Column("Nombre", TypeName = "varchar", Order = 2)]
        public string Nombre { getset; }
 
        [MaxLength(100), MinLength(10)]
        public string Descripcion { getset; }
 
        [NotMapped]
        public string CodigoIso { getset; }
    }

Ahora homologaremos todo lo que hicimos con Data Annotations a través de Fluent Api, de la siguiente manera:

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // Aquí haremos nuestras configuraciones con Fluent API.
 
            // Especificar el nombre de una tabla.
            modelBuilder.Entity<Producto>().Map(m => m.ToTable("Productos"));
 
            // establecer una primary key.
            modelBuilder.Entity<Producto>().HasKey(c => c.Codigo);
 
            // Definir un campo como requerida.
            modelBuilder.Entity<Producto>().Property(c => c.Nombre).IsRequired();
 
            // Definir el nombre de un campo.
            modelBuilder.Entity<Producto>().Property(c => c.Nombre).HasColumnName("Nombre");
 
            // Definir el tipo de un campo.
            modelBuilder.Entity<Producto>().Property(c => c.Nombre).HasColumnType("varchar");
 
            // Definir el orden de un campo.
            modelBuilder.Entity<Producto>().Property(c => c.Nombre).HasColumnOrder(2);
 
            // Definir el máximo de caracteres permitidos para un campo.
            modelBuilder.Entity<Producto>().Property(c => c.Descripcion).HasMaxLength(100);
 
            // indicar que no se debe mapear una pripiedad a la base de datos.
            modelBuilder.Entity<Producto>().Ignore(c => c.CodigoIso);
 
            base.OnModelCreating(modelBuilder);
        }

Como podemos ver, hemos homologado las instrucciones de primary key, nombres, requerido y demás características a través de Fluent Api, con esto podríamos quitar los Data Annotations de nuestra entidad de dominio Producto, y una vez se inicialice la base de datos se tendrán en cuenta las configuraciones especificadas en el método OnModelCreating.

Para terminar con el artículo quisiera darles un tip, para el trabajo con Code First Fluent Api, de una forma más ordenada, ya que como vemos la codificación en el método OnModelCreating puede crecer bastante ya que allí va la configuración para cada entidad, por esto una buena práctica es separar en clases la configuración de cada entidad, con esto logramos un código más ordenado y más reutilizable, ahora veamos cómo hacerlo:

Para esto sólo debemos crear una clase que herede de EntityTypeConfiguration<T> y en el constructor de esta clase codificar las configuraciones tal y como lo hicimos en el método OnModelCreating, cómo se muestra a continuación:

    public class ProductoMappings : EntityTypeConfiguration<Producto>
    {
        public ProductoMappings()
        {
            // Especificar el nombre de una tabla.
            this.Map(m => m.ToTable("Productos"));
 
            // establecer una primary key.
            this.HasKey(c => c.Codigo);
 
            // Definir un campo como requerida.
            this.Property(c => c.Nombre).IsRequired();
 
            // Definir el nombre de un campo.
            this.Property(c => c.Nombre).HasColumnName("Nombre");
 
            // Definir el tipo de un campo.
            this.Property(c => c.Nombre).HasColumnType("varchar");
 
            // Definir el orden de un campo.
            this.Property(c => c.Nombre).HasColumnOrder(2);
 
            // Definir el máximo de caracteres permitidos para un campo.
            this.Property(c => c.Descripcion).HasMaxLength(100);
 
            // indicar que no se debe mapear una pripiedad a la base de datos.
            this.Ignore(c => c.CodigoIso);
        }
    }

Haríamos estos para cada entidad, y en el método OnModelCreating sólo agregaríamos la configuración que acabamos de crear, de la siguiente forma:

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // Aquí haremos nuestras configuraciones con Fluent API.
 
            modelBuilder.Configurations.Add(new ProductoMappings());
 
            base.OnModelCreating(modelBuilder);
        }

Y para despedirme quisiera anotar que un método de configuración no excluye al otro, es decir podríamos usar configuración a través de Data Annotations y también a través de Fluent Api sin ningún problema. Y bueno eso es todo espero les sea de utilidad y de interés este tema de configuración de entidades de dominio a través de Entity Framework Code First Fluent Api.

Saludos y buena suerte!

6 comentarios:

  1. Code First y Fluent API pueden parecer complejos al principio y es algo difícil empezar a utilizarlos, pero una vez se implementa permite implementar el Domain Driven Design de una manera muy ágil y flexible, los beneficios que se tienen en toda la solución valen la pena

    ResponderEliminar
    Respuestas
    1. De acuerdo contigo Manuel, en mi opinión Code First es el enfoque de Entity Framework que requiere una mayor curva de aprendizaje, al igual que usar Fluent Api y no Data Annotations, pero trae consigo muchas ventajas, el hecho de tener control total sobre tu código y no trabajar con código generado es una de ellas.

      Eliminar
  2. Excelente post amigo, me gusta más utilizar fluent api ya que aisla todo de las entidades, así tenemos entidades más limpias.

    ResponderEliminar
    Respuestas
    1. Hola Julio, así es, es una gran ventaja de usar Fluent Api, es una gran alternativa.

      Gracias por leerme, saludos!

      Eliminar
  3. Este comentario ha sido eliminado por el autor.

    ResponderEliminar