Introducción
Cuando se modelan entidades suele plantearse la necesidad de definir propiedades que se agrupen para definir un tipo de dato, ejemplo de estos podrían ser los datos de contacto, direcciones, etc
Estos tipos de dato no tienen una entidad por si mismos, o sea no tienen un id o código que los identifique, sino que son parte de otra entidad.
La idea es poder representar algo como lo siguiente
Las entidades de proveedores y empleados se asocian a otras que permiten agrupar propiedades bajo un mismo concepto.
Pero a nivel de persistencia la vista es bastante diferente
Los campos que se agrupan en entidades como ser Localidad o Contacto ahora son campos individuales en cada tabla.
Existen varias formas de definir este tipo de persistencia en Entity Framework según se la quiere utilizar para una sola entidad o compartir entre varias.
Definición de las entidades
Tanto la entidad empleado como el proveedor defienden un grupo de propiedades que representa la localización, pero el proveedor además define un grupo adicional denominado contacto
La definición del proveedor se realiza en Supplier.cs
public class Supplier { public Supplier() { this.Localization = new Localization(); this.Contact = new Contact(); } public int Codigo { get; set; } public string CompanyName { get; set; } public Localization Localization { get; set; } public Contact Contact { get; set; } } public class Contact { public string ContactName { get; set; } public string ContactTitle { get; set; } public string Phone { get; set; } public string Fax { get; set; } public string HomePage { get; set; } public bool HasValue { get { return this.ContactName != null || this.ContactTitle != null || this.Phone != null || this.Fax != null || this.HomePage != null; } } }
La definición de empleado se realiza en Employee.cs
public class Employee { public Employee() { this.Localization = new Localization(); } public int EmployeeID { get; set; } public string LastName { get; set; } public string FirstName { get; set; } public Localization Localization { get; set; } }
Y ambos hacen uso de la clase que define la localización, definida en LocationComplexType.cs
public class Localization { public string Address { get; set; } public string City { get; set; } public string Region { get; set; } public string PostalCode { get; set; } public string Country { get; set; } public bool HasValue { get { return this.Address != null || this.City != null || this.Region != null || this.PostalCode != null || this.Country != null; } } }
Toda esta separación se realiza para demostrar que hay varias formas forma de separar la clase, se puede realizar físicamente en un .cs o puede definirse junto a la clase que la utiliza.
Definición básica del modelo
Si solo definimos las entidades sin ningún otro tipo de especificación.
public class NorthWindContext : DbContext { public NorthWindContext() : base("NorthwindDb") { } public DbSet<Supplier> Suppliers { get; set; } public DbSet<Employee> Employees { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); } }
creara los siguiente estructura de datos
Aplicara la definición por convención para los campos a los cuales les agregara un prefijo según el tipo complejo donde estén definida la propiedades.
Sin definir prácticamente nada ya obtenemos un modelo de datos en donde EF aplica las convenciones, pero puede que esto no nos guste por lo que a continuación vamos a ver como personalizar la persistencia de la entidad.
Definir ComplexType para una sola entidad
Analicemos la entidad Supplier, en la cual se puede especificar la definición del tipo complejo directamente en la entidad, es por eso que se utiliza:
Property(x => x.Contact.ContactName)…
Se accede a la propiedad que define la clase compleja y se mapea cada una de las propiedad.
public class SupplierMap : EntityTypeConfiguration<Supplier> { public SupplierMap() { HasKey(x => x.SupplierID); Property(x => x.CompanyName).HasMaxLength(40).IsRequired(); Property(x => x.Contact.ContactName).HasColumnName("ContactName").HasMaxLength(30); Property(x => x.Contact.ContactTitle).HasColumnName("ContactTitle").HasMaxLength(30); Property(x => x.Contact.Phone).HasColumnName("Phone").HasMaxLength(24); Property(x => x.Contact.Fax).HasColumnName("Fax").HasMaxLength(24); Property(x => x.Contact.HomePage).HasColumnName("HomePage").HasColumnType("ntext"); } }
public class NorthWindContext : DbContext { public NorthWindContext() : base("NorthwindDb") { } public DbSet<Supplier> Suppliers { get; set; } public DbSet<Employee> Employees { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new SupplierMap()); base.OnModelCreating(modelBuilder); } }
la línea:
modelBuilder.Configurations.Add(new SupplierMap());
define la clase que tiene las especificaciones del mapping de la entidad
Estas modificaciones cambian el aspecto de la tabla generada en base al modelo
Definir ComplexType compartido entre entidades
Tanto la entidad Supplier como Employee definen un tipo complejo en común, definido en la clase Localization.
El mapping de esta entidad puede realizarse de dos formas:
- Se puede definir mediante ComplexTypeConfiguration<> en el OnModelCreating()
public class NorthWindContext : DbContext { public NorthWindContext() : base("NorthwindDb") { } public DbSet<Supplier> Suppliers { get; set; } public DbSet<Employee> Employees { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new SupplierMap()); ComplexTypeConfiguration<Localization> complexLocalizacion = modelBuilder.ComplexType<Localization>(); complexLocalizacion.Property(x => x.Address).HasColumnName("Address").HasMaxLength(60); complexLocalizacion.Property(x => x.City).HasColumnName("City").HasMaxLength(15); complexLocalizacion.Property(x => x.Region).HasColumnName("Region").HasMaxLength(15); complexLocalizacion.Property(x => x.PostalCode).HasColumnName("PostalCode").HasMaxLength(10); complexLocalizacion.Property(x => x.Country).HasColumnName("Country").HasMaxLength(15); modelBuilder.Configurations.Add(new EmployeeMap()); base.OnModelCreating(modelBuilder); } }
- Definir una clase de mapping que especifique la configuración
public class NorthWindContext : DbContext { public NorthWindContext() : base("NorthwindDb") { } public DbSet<Supplier> Suppliers { get; set; } public DbSet<Employee> Employees { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new SupplierMap()); modelBuilder.Configurations.Add(new LocationComplexMap()); modelBuilder.Configurations.Add(new EmployeeMap()); base.OnModelCreating(modelBuilder); } }
public class LocationComplexMap : EntityTypeConfiguration<Localization> { public LocationComplexMap() { Property(x => x.Address).HasColumnName("Address").HasMaxLength(60); Property(x => x.City).HasColumnName("City").HasMaxLength(15); Property(x => x.Region).HasColumnName("Region").HasMaxLength(15); Property(x => x.PostalCode).HasColumnName("PostalCode").HasMaxLength(10); Property(x => x.Country).HasColumnName("Country").HasMaxLength(15); } }
Por medio de la línea:
modelBuilder.Configurations.Add(new LocationComplexMap());
se asocia la clase de mapping con la definición del contexto
La ejecución del Test generara en la base de datos las tablas con al estructura y tipos de datos que buscamos:
Test - Recuperar todos los proveedores
Los diferentes test que confeccionemos nos ayudara a validar la definición del mapping de cada entidad
Recuperamos la lista de todos los proveedores
La ejecución del test inserta un nuevo proveedor
Y luego lo recupera
Se puede ver a simple fácilmente como se incluye en las queries los campos que forman las entidad complejas.
Test - Recuperar un solo empleado
En el test del empleado se crea una nueva entidad empleado, esto luego se recupera para validar que los campos son correctamente mapeados contra la tabla.
Al igual que en el test anterior la consulta filtra por el id de la entidad que se quiere recuperar incluyendo los campos que conforman la entidad mas los que definen los tipos complejos.
Código
Se utiliza Visual Studio 2012, la base de datos es creada por el mismo Entity Framework cuando se ejecutan los test
[C#]
|
Buenisimo! la verdad que si haces articulos como estos mas seguido dejo la facultad!! :)
ResponderEliminarLeo ! te hago una consulta, si queres setear una columna como unique o darle un valor default, lo podes hacer, en el archivo de mapeo de cada entidad o lo tenes que hacer en la bd?
ResponderEliminarhola Patos
ResponderEliminaraqui
http://social.msdn.microsoft.com/Forums/es-ES/0a2e2ac6-f78b-40e9-bb25-c72bdba755a9/indice-uniqe-con-fluent-api-code-first
respondi sobre el mismo tema
saludos
Hola Leandro, gracias por el articulo, en la clase Supplier esta la propiedas Codigo pero deberia ser SupplierID como esta en el codigo de ejemplo.
ResponderEliminarhola andres
ResponderEliminartienes razon en las primeras imagenes se puede visualizar la propiedad Codigo en lugar de SupplierID, es que al principio queria plantear el articulo mostrando la definicion por fuera de la convencion que define EF y despues lo cambie, pero olvide actualizar esa parte
lo voy a editar asi lo defino de forma correcta
gracias por la correccion
saludos
Thanks for sharing, nice post!
ResponderEliminarPhục vụ cho nhu cầu vận chuyển container bằng đường bộ ngày càng lớn, vận chuyển xăng dầu bằng đường sắt và vận tải, gửi hàng hóa vận chuyển xe máy bắc nam bằng đường sắt cũng đã xây dựng nên những qui trình, dịch vụ vận chuyển hàng hóa bằng các toa xe chuyên dùng chuyên nghiệp và có hệ thống. Đảm bảo mang đến chất lượng tốt nhất cho khách hàng sử dụng dịch vụ.