2.27.2011

El Entity Framework en una Arquitectura n-Layer – Parte 6–{ Herencia en las Entidades}

Cuando se utiliza el EntityFramework normalmente generamos el modelo en la capa de acceso a datos y procedemos a trabajar directamente con las entidades generadas desde la base de datos. Sin embargo, al estar trabajando con Entidades, estamos trabajando directamente con el estado del objeto de negocios generado a partir de la tabla asociada, con lo cual podemos aprovecharnos de todas las bondades del mundo de la orientación a objetos. Quizás la más obvia es la herencia, en donde podemos crear nuestras entidades sobre modelos de herencia para crear un esquema de entidades de trabajo más simple y más comprensible.

Ejemplo

Para ver la herencia funcionando con el Entity Framework vamos a crear un ejemplo en donde vamos a tener dos tipos de empleado: El empleado de planta y el empleado contratista o consultor. El esquema de base de datos sobre el cual vamos a basar este ejemplo es el siguiente:

image

Nótese que en este esquema vamos a tener una tabla para manejar empleados, esta tabla tiene una asociación con una tabla empresa; tanto el empleado de planta como el empleado contratista deben de tener una empresa asociada { en el empleado de planta porque puede ser que la empresa sea un conglomerado de empresas y hay que identificar cual empresa es la que lo tiene en planilla}. Además, podemos ver que en la tabla empleado existe el campo salario y el campo costo por hora. El salario es el asignado al empleado de planta ya que la empresa si conoce cuanto se le paga; sin embargo, al empleado contratisa no se conoce cuanto le paga la empresa contratista por lo tanto se le asigna el costo por hora que cobra la empresa contratista por cada empleado.

Para crear la aplicación procedemos a crear un proyecto en WPF de la siguiente forma:

image

Observemos que esta vez creamos el UI en WPF sin embargo la solución solamente se llama Herencia.EF. El siguiente paso es crear una librería para la capa de acceso a datos – para ver como se crea ver este post –, luego procedemos a agregar el esquema de base de datos antes creado utilizando un modelo del Entity Framework.

image

En nuestro caso el esquema generado es el siguiente:

image

Agregando la Herencia

Ahora vamos a proceder a agregar la herencia de entidades en nuestro modelo. Vamos inicialmente a crear dos entidades más: el empleado contratista y el empleado de planta. Para esto procedemos a dar clic derecho sobre el modelo generado por el Entity Framework y seleccionamos la opción de agregar Entidad.

image

Procedemos primero a agregar el empleado contratista

image

Luego procedemos a agregar el empleado de planta.

image

Luego de esto el diagrama del modelo del Entity Framework nos debería quedar así:

image

Luego procedemos a cortar las propiedades específicas para cada empleado de la clase base para especializar cada una de las entidades.

image

El siguiente paso es crear los mapas para que el Entity Framework tenga noción de como funciona la herencia especificada. En este paso, tenemos que crear los mapas necesarios para indicarle al Entity Framework en que momento se utiliza una instancia de EmpleadoDePlanta y en que circunstancia un EmpleadoContratista. Primero vamos a seleccionar EmpleadoDePlanta, luego vamos a agregar un mapeo en la ventana de Detalles de los mapas; para lograr esto, una vez seleccionado la entidadEmpleadoDePlanta procedemos a agregar un nuevo mapa justo debajo de la definición de la tabla, en este nuevo mapa agregamos la condición del mapa, la cual sería que cuando el campo TipoEmpleado sea igual a “empleado” entonces proceda con una instancia de EmpleadoDePlanta.

image

Al igual que hicimos con el EmpleadoDePlanta, ahora procedemos a crear un mapeo para el empleado contratista pero esta vez el mapeo de la columna la hacemos a través de la propiedad CostoXHora

image

image

Una vez agregado estos mapeos de herencia todavía nos falta reparar unos errores que se dan porque cambiamos de una forma no tan inferible el comportamiento de estas entidades. El primero error es el siguiente:

image

Esto se da porque a la hora de crear la instancia de la entidad y basados en la condición de mapeo para la columna TipoEmpleado, no hemos definido que instancia se debe de crear cuando no se tiene un valor en TipoEmpleado, o mejor dicho, no hemos definido cuando se debe crear una instancia de Empleado. Hay dos formas de repararlo; la primera creando un valor para cuando no se quiere un empleado específico y la segunda crear el tipo Empleado como abstracto para que no se pueda instanciar, solamente se pueda a heredar, y en nuestro caso vamos a utilizar la segunda opción. Para esto procedemos a marcar la clase empleado, vamos a las propiedades y ahi buscamos la propiedad Abstract y le establecemos el valor en true.

image

Si volvemos a compilar obtenemos otro error, el cual es un poco más ambiguo.

image

En este caso, el error se da porque la variable TipoEmpleado se mapea automáticamente a la hora de crear las instancias de las clases que heredan de empleado y por lo tanto no debe de estar disponible para ser modificada, por esta razón lo único que debemos hacer en este caso es remover la propiedad TipoEmpleado. El nuevo esquema es el siguiente:

image

Ahora vamos a proceder a utilizar nuestro modelo. Como hemos hecho en post anteriores acerca de aplicaciones n-layer con el entity framework procedemos a crear una capa de lógica de negocios desde donde estaremos creando nuestros métodos para obtener empleados de planta y empleados Contratistas.

public class EmpleadoBL
{
Ejemplos_BlogEntities1 _contexto = new Ejemplos_BlogEntities1();

public List<EmpleadoDePlanta> ObtenerEmpleados( )
{
var empleadosDePlanta = _contexto.Empleado.OfType<EmpleadoDePlanta>();
return empleadosDePlanta.ToList();
}

public List<EmpleadoContratista> ObtenerEmpleadosContratistas( )
{
var empleadosContratistas = _contexto.Empleado.OfType<EmpleadoContratista>();
return empleadosContratistas.ToList();
}
}

Es de resaltar el uso del método de extensión OfType a través del cual le solicitamos explícitamente al Entity Framework cual tipo es el que queremos y por lo tanto el debe mapear. Seguidamente procedemos a dibujar la pantalla en WPF a través de la cual vamos a desplegar los datos.


image


Esta pantalla aunque muy sencilla nos servirá para ilustrar el uso de los métodos anteriores y la respectiva herencia en el Entity Framework. El código en cada uno de los RadioButton es el siguiente:

private void rdRegular_Checked( object sender, RoutedEventArgs e )
{
if (dgEmpleaddos != null)
{
EmpleadoBL empleadoBL = new EmpleadoBL();
dgEmpleaddos.ItemsSource = empleadoBL.ObtenerEmpleados();
}
}

private void rdContratista_Checked( object sender, RoutedEventArgs e )
{
if (dgEmpleaddos != null)
{
EmpleadoBL empleadoBL = new EmpleadoBL();
dgEmpleaddos.ItemsSource = empleadoBL.ObtenerEmpleadosContratistas();
}
}

Si ejecutamos este código, nos vamos a dar cuenta que el Entity Framework resuelve correctamente nuestras consultas de acuerdo al tipo solicitado en cada método invocado.


Aquí estamos solicitando ver los empleados de planta y como se aprecia en la foto el primer campo que nos aparece es el salario.


image


En la siguiente imagen estamos solicitando ver los contratistas y por lo tanto nos aparece el costo por hora de cada empleado.


image





2.24.2011

Recorriendo una Lista con el ForEach pero sin un for each en C#

Cada vez que utilizamos listas en nuestras aplicaciones existe una altísima probabilidad de tengamos que recorrer la lista para llevar a cabo una acción específica sobre cada uno de los elementos contenidos en la misma. Esta acción se realiza normalmente utilizando la estructura for each o un for tradicional estilo “C++”. Sin embargo; en .NET las listas tienen otra forma de recorrer todos sus elementos y es utilizando el método Lista.ForEach( accion ). Este método me permite definir un delegate genérico para el método y asignarlo como acción, con lo cual cada elemento contenido en la lista es enviado como parámetro al delegate y se ejecuta el método; en otras palabras, el método se ejecuta una vez por cada elemento en la lista.

Ejemplo

Como siempre, para acompañar la teoría vamos a agregar un ejemplo. Supongamos que tenemos una lista de personas y otra lista de personas aprobadas; además, la lista de personas aprobadas solo se puede llenar con las personas que sean mayores de 18 años. Para esto primero vamos a crear una clase persona.

Code Snippet
  1. public class Persona
  2. {
  3.     public int Id { get; set; }
  4.     public string Nombre { get; set; }
  5.     public int Edad { get; set; }
  6. }

El siguiente paso es crear dos variables a nivel de clase – estáticas para no agrandar demasiado el ejemplo – y pertenecientes a la clase program.

Code Snippet
  1. class Program
  2.   {
  3.       static List<Persona> _solicitantes = new List<Persona>();
  4.       static List<Persona> _aprobados = new List<Persona>();

Luego procedemos a agregar unas cuantas personas a la lista de solicitantes.

Code Snippet
  1. static void Main(string[] args)
  2.       {
  3.          
  4.  
  5.           Persona p0 = new Persona
  6.           {
  7.               Id = 1,
  8.               Nombre = "Estela",
  9.               Edad = 18
  10.           };
  11.  
  12.           Persona p1 = new Persona {
  13.               Id = 2,
  14.               Nombre = "Luis",
  15.               Edad = 25
  16.           };
  17.  
  18.           Persona p2 = new Persona
  19.           {
  20.               Id = 3,
  21.               Nombre = "Erick",
  22.               Edad = 17
  23.           };
  24.  
  25.           Persona p3 = new Persona
  26.           {
  27.               Id = 4,
  28.               Nombre = "Carlos",
  29.               Edad = 35
  30.           };
  31.  
  32.           Persona p4 = new Persona
  33.           {
  34.               Id = 5,
  35.               Nombre = "Ana",
  36.               Edad = 16
  37.           };
  38.  
  39.           _solicitantes.Add(p0);
  40.           _solicitantes.Add(p1);
  41.           _solicitantes.Add(p2);
  42.           _solicitantes.Add(p3);
  43.           _solicitantes.Add(p4);

Como podemos ver, ahora tenemos un par de personas que no tienen los 18 años. Ahora vamos a proceder a recorrer la lista y verificar cual persona es mayor de 18 años para agregarla a lista de aprobados, para esto primero tenemos que crear el delegate que hará el procesamiento.

Code Snippet
  1. private static void ProcesarSolicitante(Persona pActual)
  2. {
  3.     if (pActual.Edad > 17)
  4.     {
  5.         _aprobados.Add(pActual);   
  6.     }
  7. }

Ahora, para imprimir la lista, también voy a utilizar un delegate, tal y como se presenta en el siguiente “code snippet”.

Code Snippet
  1. private static void ImprimirPersona(Persona pActual)
  2.         {
  3.             Console.WriteLine("Id: {0}\nNombre: {1},\nEdad: {2}",
  4.                 pActual.Id.ToString(), pActual.Nombre, pActual.Edad.ToString());
  5.         }

Ahora en el método Main, después de agregar la última persona procedemos a invocar el método ForEach de la lista de solicitantes y le asignamos el delegate ProcesarSolicitante.

Code Snippet
  1. _solicitantes.ForEach(ProcesarSolicitante);

Luego de esto procedemos a imprimir la lista de aprobados.

Code Snippet
  1. Console.WriteLine("Usuarios Aceptados");
  2.             Console.WriteLine("------------------");
  3.  
  4.             _aprobados.ForEach(ImprimirPersona);

Al ejecutar el código, podemos ver que en la lista de aprobados solamente aparecen las personas mayores de 17 años.

image

Etiquetas de Technorati: ,,

2.15.2011

El Entity Framework en una Arquitectura n-Layer – Parte 5 {Creando la capa de Entidades}

 

Siguiendo con la serie de post respecto al EntityFramework y las arquitecturas n-layer, en este post vamos a trabajar en la creación de una capa de entidades de negocio de forma personalizada y que trabaje ligada con el EF para aprovechar sus capacidades.

En un post anterior, se mostró como trabajar una arquitectura n-layer con el entity framework; sin embargo, muchos de los lectores hicieron notar que con el enfoque actual, debíamos agregar una referencia a la capa de acceso a datos donde tenemos generado nuestros artefactos del entity framework {las entidades de negocio}, y argumentaban que se perdía la separación de capas con este enfoque. Pese a que aún en artículos de la revista del msdn magazine se indicaba que esa era la forma de hacerlo, me puse a buscar una solución para el problema y lo que se publica en este post es una forma de solucionar el problema mencionado.

Capa de Entidades: Es Necesaria?

La capa de entidades de negocio representa el estado de nuestros objetos de negocio. En esta capa se representa el estado de nuestras entidades; el comportamiento esta en la capa de lógica de negocios. ¿Y por qué de esta manera? Simple, porque cuando tengo que transportar los objetos a través de servicios solo debo transportar lo necesario, y en este caso es el estado del objeto.

Las Entidades de negocio tienen como fin transportar los datos desde el repositorio de datos hasta la capa de presentación por lo que están referenciadas en todas las demás capas. Esta capa puede ser representada por DataSets – no recomendado y establecido aquí por qué – o por POCOS, también por las entidades generadas por el entity framework. El caso con el que vamos a trabajar esta vez es con los POCOS, los cuales son muy eficientes a la hora de trabajar en arquitecturas n-layer.¿ Y por qué no utilizamos las entidades generadas por el entity framework? bueno, como dijimos anteriormente estas clases tienen una serie de instrucciones las cuales en muchos de los escenarios en donde trabajamos no las necesitamos – o no nos damos cuentan que existen y sobrevivimos –; además, para poder hacer uso de las entidades generadas por el EF, requerimos agregar la capa donde esta generado nuestro esquema a través del EF, con lo cual rompemos la independencia de las capas, puesto que requerimos agregar el DAL en el UI para poder crear instancias de las clases.

Ejemplo: Utilizando POCOs en el Entity Framework

Para utilizar POCOs con el EF, primero vamos a crear un proyecto de prueba para interactuar con solamente una tabla, la base de datos de prueba tiene el siguiente esquema.

image

Esta tabla queda mapeada de la siguiente forma utilizando el generador del Entity Framework.

image

Este esquema generado queda en el proyecto de la capa de acceso a datos.

image

Luego creamos un proyecto de Entidades de Negocio y creamos en el mismo una clase POCO llamada Producto, además agregamos una referencia a los siguientes dlls.

image

La clase producto va a representar a la entidad producto en nuestro proyecto, y para que podamos aprovechar todas las bondades del EF a la hora de interactuar con la base de datos debemos nombrar esta clase igual que la entidad. Además, debemos agregar una serie de atributos sobre el namespace, la clase y las propiedades de la clase creada para que el Entity Framework entienda el metadata de la clase y pueda tratarla como la entidad que corresponde. Además debemos agregar los namespaces resaltados

 

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. using System.Data;
  7. using System.Data.Objects.DataClasses;
  8. using System.Data.Metadata.Edm;
  9.  
  10.  
  11. [assembly: EdmSchemaAttribute()]
  12. namespace CustomClassEF.EL
  13. {
  14.     [EdmEntityTypeAttribute(NamespaceName = "Blog_SamplesModel", Name = "Producto")]
  15.     public class Producto : EntityObject
  16.     {
  17.         Int64 m_Id;
  18.         string m_Nombre;
  19.         decimal m_Precio;
  20.  
  21.         [EdmScalarPropertyAttribute( EntityKeyProperty=true, IsNullable=false)]
  22.         public Int64 Id
  23.         {
  24.             get
  25.             { return m_Id;}
  26.             set
  27.             {
  28.                 ReportPropertyChanging("Id");
  29.                 m_Id = value;
  30.                 ReportPropertyChanged("Id");
  31.             }
  32.         }
  33.  
  34.         [EdmScalarPropertyAttribute( IsNullable = false)]
  35.         public string Nombre
  36.         {
  37.             get
  38.             {
  39.                 return m_Nombre;
  40.             }
  41.             set
  42.             {
  43.                 ReportPropertyChanging("Nombre");
  44.                 m_Nombre = value;
  45.                 ReportPropertyChanged("Nombre");
  46.             }
  47.         }
  48.  
  49.         [EdmScalarPropertyAttribute(IsNullable = false)]
  50.         public decimal Precio
  51.         {
  52.             get
  53.             {
  54.                 return m_Precio;
  55.             }
  56.             set
  57.             {
  58.                 ReportPropertyChanging("Precio");
  59.                 m_Precio = value;
  60.                 ReportPropertyChanged("Precio");
  61.             }
  62.         }
  63.  
  64.         public Producto()
  65.         { }
  66.  
  67.     }
  68. }

El primer atributo EdmSchemaAttribute indica que el assembly tiene clases que están mapeadas a entidades generadas por el Entity Framework en el modelo conceptual. El atributo EdmEntityTypeAttribute indica que la clase siguiente representa un tipo de una entidad. Seguidamente ponemos nuestra clase producto a heredar de la clase EntityObject, con lo cual tenemos todas las funcionalidades de tracking e interacción con la base de datos a través del EF. Luego utilizamos el atributo EdmScalarPropertyAttribute para indicar que la propiedad representa un tipo escalar; en el caso de la llave primaria se le indica a através del parámetro EntityKeyProperty que la propiedad indicada es la llave primaria, además con el parámetro IsNullable se le indica si el tipo a usar soporta nulos o no en la base de datos. Por último procedemos a reportar el inicio y finalización del cambio del valor de cada una de las propiedades en el set de la misma. Con esto ya tenemos nuestra clase configurada para interactuar con el contexto generado por el EntityFramework y nuestra nueva clase puede reemplazar sin ningún problema a la entidad producto.

Luego procedemos a crear el proyecto de lógica de negocios en donde además de la clase ProductoBL, procedemos a agregar las siguientes referencias:

image

El código de la clase ProductoBL es el siguiente:

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using CustomClassEF.DAL;
  6. using CustomClassEF.EL;
  7.  
  8. namespace CustomClassEF.BL
  9. {
  10.  
  11.     public class ProductoBL
  12.     {
  13.         DAL.Blog_SamplesEntities m_Context = new Blog_SamplesEntities();
  14.  
  15.         public DAL.Blog_SamplesEntities Context
  16.         {
  17.             get { return m_Context; }
  18.             set { m_Context = value; }
  19.         }
  20.  
  21.         public int AgregarProducto(EL.Producto _producto)
  22.         {
  23.             Context.AddObject("Producto", _producto);
  24.             return Context.SaveChanges();
  25.         }
  26.  
  27.  
  28.     }
  29. }

Como podemos ver en el método AgregarProducto, estamos utilizando un parámetro de tipo EL.Producto y no del tipo DAL.Producto. Esto nos indica que estamos esperando una instancia del tipo del POCO y no del tipo de la entidad generada. Además, con el método AddObject procedemos a agregar el POCO y no a convertir el POCO en una entidad.

Luego en la capa de presentación creamos el siguiente proyecto:

image

En nuestro caso creamos una aplicación WPF y como se puede ver en la figura anterior solo hacemos referencia al BL y al EL. Además agregamos un app.config para el manejo de la conexión. La pantalla creada en este proyecto es la siguiente:

image

El código del botón Ok para proceder a salvar el producto creado es el siguiente:

Code Snippet
  1.  
  2. using CustomClassEF.EL;
  3. using CustomClassEF.BL;
  4.  
  5. namespace CustomClassEF.UI
  6. {
  7.     /// <summary>
  8.     /// Interaction logic for MainWindow.xaml
  9.     /// </summary>
  10.     public partial class MainWindow : Window
  11.     {
  12.         public MainWindow()
  13.         {
  14.             InitializeComponent();
  15.         }
  16.  
  17.         private void btnGuardar_Click(object sender, RoutedEventArgs e)
  18.         {
  19.             Producto _producto = new Producto
  20.             {
  21.                 Nombre = txtNombre.Text,
  22.                 Precio = Convert.ToDecimal(txtPrecio.Text)
  23.             };
  24.             ProductoBL _productoBL = new ProductoBL();
  25.             _productoBL.AgregarProducto(_producto);
  26.         }
  27.     }
  28. }

Como podemos ver no agregamos ninguna referencia al DAL, y el comportamiento de la aplicación no sufre ningún cambio, ya que el POCO recién creado funciona perfectamente con el Entity Framework. Nótese que no hemos creado ninguna relación entre entidades todavía – algo que espero hacer en post posteriores – ya que para esto se deben agregar más atributos a los POCOs para hacer que las relaciones y la carga de datos funcione correctamente.