8.25.2010

Seminario de SOA

Aprovecho este espacio para invitarlos a un seminario de Arquitectura Orientada a Servicio, el cual tengo el privilegio de dar el día 1ero de octubre en la Ulacit. Ese día estaremos utilizando el NServiceBus para demostrar como trabajar con un ESB en nuestra organización y además para aclarle a todos aquellos que todavía no lo tienen claro, que para tener una arquiectura orientada a servicios se requieren servicios, no solamente servicios Web. Cualquier consulta comunicarse conmigo o en los teléfonos y correos del anuncio.

SOA Anuncio

Technorati Tags: ,

8.22.2010

AutoMapper 2 – Trabajando con Conversiones de Varios Niveles

Siguiendo con el post anterior, Automapper no solamente nos permite mapear clases con una relación de 1 a 1. En este post vamos a ver como podemos totalizar de diversas formas una relación entre dos clases utilizando AutoMapper. En primera instancia vamos a suponer una relación entre dos clases, una llamada Tarea la cual contiene información referente a las tareas que componen un proyecto y cuya estructura se puede ver aca.

public class Tarea
{
public int Id { get; set; }
public string Nombre { get; set; }
public double HorasAsignadas { get; set; }
public double CostoXHora { get; set; }

public double GetTotal()
{
return HorasAsignadas * CostoXHora;
}
}


En este caso simplemente tenemos 4 propiedades automáticas y una función que me retorna el costo total de esta tarea. Seguidamente vamos a tener una clase llamada proyecto, la cual contiene una lista de tareas, y dos métodos que nos permiten ver el total de horas del proyecto y el costo total en horas del proyecto. Ambos métodos utilizan el método de extensión SUM de las listas en .NET.



public class Proyecto
{
public int Id { get; set; }
public string Nombre { get; set; }
public List<Tarea> TareasDelProyecto { get; set; }
public DateTime FechaInicio { get; set; }
public DateTime FechaFinal { get; set; }

public double GetTotal()
{
return TareasDelProyecto.Sum(tarea => tarea.GetTotal());
}

public double ObtenerTotalHorasDelProyecto()
{
return TareasDelProyecto.Sum(tarea => tarea.CostoXHora);
}

public Proyecto()
{
TareasDelProyecto = new List<Tarea>();

}
}



Seguidamente vamos a mostrar la clase a la que quiero mapear las clases anteriores. Esta clase lo que quiero que tenga es un resumen del proyecto en lo que respecta a costos y tiempo, por lo tanto, esta clase va a tener una propiedad nombre para identificar el proyecto, una campo para mapear el total del horas empleadas en el proyecto, y un campo para mapear el costo total en horas del proyecto.



public class CostoTotalDeProyecto
{
public string NombreProyecto { get; set; }
public double TotalHorasDelProyecto { get; set; }
public double CostoTotalEnHorasDeProyecto { get; set; }
}



Ahora vamos a configurar AutoMapper para que me haga el mapeo de estas clases.



Mapper.CreateMap<Proyecto, CostoTotalDeProyecto>()
.ForMember(dest => dest.NombreProyecto, src => src.MapFrom(fuente => fuente.Nombre))
.ForMember(dest => dest.TotalHorasDelProyecto, src => src.MapFrom( fuente=> fuente.ObtenerTotalHorasDelProyecto()))
.ForMember(dest => dest.CostoTotalEnHorasDeProyecto, src => src.MapFrom ( fuente => fuente.GetTotal()));



Luego creamos las tres instancias para la clase tarea y se las agregamos a la clase proyecto.



Tarea t1 = new Tarea
{
Id = 1,
Nombre = "Reunión Diaria Seguimiento",
HorasAsignadas = 1,
CostoXHora = 20
};

Tarea t2 = new Tarea
{
Id = 2,
Nombre = "Diseño del plano",
HorasAsignadas = 80,
CostoXHora = 25
};

Tarea t3 = new Tarea
{
Id = 3,
Nombre = "Revisión de Avance",
HorasAsignadas = 160,
CostoXHora = 20
};

Proyecto p = new Proyecto
{
Id = 1,
Nombre = "Construcción Casa",
FechaInicio = DateTime.Now,
FechaFinal = DateTime.Now.AddDays(160)
};

p.TareasDelProyecto.Add(t1);
p.TareasDelProyecto.Add(t2);
p.TareasDelProyecto.Add(t3);


Ahora procedemos a generar el mapeo de la clase. Este procedimiento es el mismo que habiamos visto en el post antes mencionado.



CostoTotalDeProyecto cp = Mapper.Map<Proyecto, CostoTotalDeProyecto>(p);



Por último, vamos a escribir en la consola el resultado del mapeo.



Console.WriteLine("Nombre del Proyecto: {0} ", cp.NombreProyecto);
Console.WriteLine("Total de Horas del Proyecto: {0}", cp.TotalHorasDelProyecto.ToString());
Console.WriteLine("Costo total en horas del proyecto: {0}", cp.CostoTotalEnHorasDeProyecto.ToString());
Console.ReadLine();



El resultado de ejecutar este código se puede ver en la siguiente imagen.



image



Ahora, supongamos que no tenemos la posiblidad de tener los métodos de la clase proyecto que sumarizan los resultados que queremos exponer desde nuestro mapeo, y que lo único que vamos a tener en la clase Proyecto es el estado del objeto. En este caso, vamos a modificar las clases iniciales para que luzcan así:



public class Tarea
{
public int Id { get; set; }
public string Nombre { get; set; }
public double HorasAsignadas { get; set; }
public double CostoXHora { get; set; }

}

public class Proyecto
{
public int Id { get; set; }
public string Nombre { get; set; }
public List<Tarea> TareasDelProyecto { get; set; }
public DateTime FechaInicio { get; set; }
public DateTime FechaFinal { get; set; }

public Proyecto()
{
TareasDelProyecto = new List<Tarea>();

}
}


Como se puede ver en el código anterior, ya la clases no contiene los métodos que nos ayudan a sumarizar los valores que deseamos ver en la clase mapeada, es decir, total de horas del proyecto y costo total en horas del proyecto.  Para lograr que el mapeo funcione, movemos las operaciones de sumatoria a la expresión lambda  tal y como se muestra a continuación



Mapper.CreateMap<Proyecto, CostoTotalDeProyecto>()
.ForMember(dest => dest.NombreProyecto, src => src.MapFrom(fuente => fuente.Nombre))
.ForMember(dest => dest.TotalHorasDelProyecto, src =>
src.MapFrom(fuente => fuente.TareasDelProyecto.Sum(tarea => tarea.CostoXHora)))
.ForMember(dest => dest.CostoTotalEnHorasDeProyecto,
src => src.MapFrom(fuente => fuente.TareasDelProyecto.Sum(tarea => tarea.HorasAsignadas * tarea.CostoXHora)));



Como podemos ver en el texto en negrita, la expresión para procesar una sumatoria tanto del costo por hora del proyecto como del total de horas que requiere el proyecto se realiza de forma simple en el mapeo sin necesidad de depender de las clases para que ellas realicen este cálculo. El resultado de ejecutar esta operación es el siguiente:



image



Como podemos ver, el resultado es exactamente igual a que nos dio la ejecución anterior cuando utilizabamos las operaciones de totalización dentro de las clases.



Technorati Tags: ,,

8.08.2010

¿Qué es AutoMapper?

Según la documentación, AutoMapper es Mapeador de Objeto a Objeto, es decir recibo una instancia de un objeto y genera otra instancia de otro objeto con diferente estructura. Interesante no? pues resulta que esta herramienta gratuita, disponible en codeplex - http://automapper.codeplex.com/ – es una librería que nos puede ahorra muchísimo código cuando estamos desarrollando aplicaciones .NET.

Este producto tiene muchas características que se pueden aplicar en el día a día, ya que la conversión de objetos es algo que hacemos casi de forma “instintiva” en nuestro esfuerzo por desarrollar software. Por ejemplo, supongamos que tenemos un servicio que retorna un tipo específico que existe en nuestro dominio de negocio y que sin embargo contiene propiedades que no queremos exponer a los que estén interesados en consumir este servicio. Esta entidad es un empleado que nuestra empresa da en outsourcing a otras compañías. Como se puede suponer, la empresa no tiene interés de exponerle al comprador del servicio cuánto gana ni el nivel que tiene catalogado dentro de la organización. Para lograr esto con automapper, iniciamos por definir las clases que vamos a utilizar. La primera Empleado que es la clases interna, y la segunda EmpleadoOferta que es la clase que le vamos a presentar al cliente.

public class Empleado
{
public int Id { get; set; }
public string Nombre { get; set; }
public string Especialidad { get; set; }
public double SalarioXHora { get; set; }
public string Nivel { get; set; }
public double Experiencia { get; set; }

}



public class EmpleadoOferta
{
public int Id { get; set; }
public string Nombre { get; set; }
public string AnnosExperiencia { get; set; }
public string Especialidad { get; set; }
}



Seguidamente en el método Main de una aplicación de consola ejemplo, procedemos a crear instancias de la clase empleado y las auto inicializamos



static void Main(string[] args)
{
Empleado empleado1 = new Empleado
{
Id = 1,
Nombre = "Gabriel Fernandez",
Especialidad = "ASP.NET",
Experiencia = 4,
Nivel = "Sennior 1",
SalarioXHora = 14
};
Empleado empleado2 = new Empleado
{
Id = 2,
Nombre = "Gabriela Montés",
Experiencia = 2,
Especialidad = "Silverlight",
Nivel = "Junior 3",
SalarioXHora = 9
};


Ahora si, llegó el momento de utilizar automapper, en este caso vamos a configurar por código la transformación deseada.



//Se configura el mapeo
Mapper.CreateMap<Empleado, EmpleadoOferta>()
.ForMember(dest => dest.Nombre, fte => fte.MapFrom(src => src.Nombre))
.ForMember(dest => dest.Id, fte => fte.MapFrom(src => src.Id))
.ForMember(dest => dest.AnnosExperiencia, fte => fte.MapFrom(src => src.Experiencia))
.ForMember(dest => dest.Especialidad, fte => fte.MapFrom(src => src.Especialidad));


Como podemos ver en el código anterior, para configurar el mapeo se utilizan expresiones lambda. En primera instancia creamos un mapa indicándole cuál es la clase fuente y cual es la clase destino



Mapper.CreateMap<Empleado, EmpleadoOferta>()



Inmediatamente después procedemos a crear el mapeo que vamos a necesitar en nuestro código; este mapeo se hace expresando a través de expresiones lambda el campo que corresponde a la clase destino, desde la clase fuente – en nuestro caso fte:



.ForMember(dest => dest.Nombre, fte => fte.MapFrom(src => src.Nombre))



Ahora creamos un método que mapea e imprime el valor de cada una de las clases, la fuente y la transformada



private static void ProcesarEmpleado(Empleado empleado1)
{
Console.WriteLine("--------------Iniciando Mapeo------------------");

Console.WriteLine("-----Empleado para uso Interno-----");
Console.WriteLine("Id Empleado {0}", empleado1.Id);
Console.WriteLine("Nombre {0}", empleado1.Nombre);
Console.WriteLine("Experiencia del Empleado {0}", empleado1.Experiencia);
Console.WriteLine("Especialidad del Empleado {0}", empleado1.Especialidad);
Console.WriteLine("Nivel del Empleado {0}", empleado1.Nivel);
Console.WriteLine("Salario por hora ${0}", empleado1.SalarioXHora);

EmpleadoOferta empleadoOferta1 = Mapper.Map<Empleado, EmpleadoOferta>(empleado1);

Console.WriteLine("----- Empleado para la Oferta -----");
Console.WriteLine("Id Empleado {0}", empleadoOferta1.Id);
Console.WriteLine("Nombre {0}", empleadoOferta1.Nombre);
Console.WriteLine("Experiencia del Empleado {0}", empleadoOferta1.AnnosExperiencia);
Console.WriteLine("Especialidad del Empleado {0}", empleadoOferta1.Especialidad);

Console.WriteLine("--------------Mapeo Finalizado------------------");
}


Quizá la parte más importante de este método es la invocación Mapper.Map, la cual como podemos ver en el código resaltado recibe vía tipos parametrizados el tipo de las clases que se desea transformar – así también por reflección puede determinar cual mapeo usar. Seguidamente procedemos a enviar por parámetro la instancia del tipo de la clase origen, en nuestro caso empleado1 que es una instancia de Empleado. Como podemos ver en el código, el método estático Map, retorna una instancia del tipo destino con la transformación aplicada, por esta razón, creamos una variable de tipo EmpleadoOferta y le asignamos el resultado del mapeo. Desde el método Main invocamos al método ProcesarEmpleado con las instancias de Empleado que creamos anteriormente.



ProcesarEmpleado(empleado1);
ProcesarEmpleado(empleado2);



El resultado de la ejecución del codigo anterior es el siguiente:



image



Ahora supongamos que queremos hacer una transformación un poquito más compleja, por ejemplo un cálculo en medio de la transformación. Supongamos que ahora ahora deseamos exponer en la nueva clase la tarifa por hora del empleado consultado. Para este caso modificamos la clase EmpleadoOferta para que tenga una propiedad más para que nos sirva para este propósito, esta propiedad es CostoAlCliente.



public class EmpleadoOferta
{
public int Id { get; set; }
public string Nombre { get; set; }
public string AnnosExperiencia { get; set; }
public string Especialidad { get; set; }
public double CostoAlCliente { get; set; }
}



En nuestro caso, vamos a agregarle un 40% de ganancia por cada hora de trabajo del recurso que el cliente desea tener, para lo cual vamos a modificar el mapeo para que la clase mapeada tenga este valor calculado. El código modificado es el siguiente:



//Se configura el mapeo
Mapper.CreateMap<Empleado, EmpleadoOferta>()
.ForMember(dest => dest.Nombre, fte => fte.MapFrom(src => src.Nombre))
.ForMember(dest => dest.Id, fte => fte.MapFrom(src => src.Id))
.ForMember(dest => dest.AnnosExperiencia, fte => fte.MapFrom(src => src.Experiencia))
.ForMember(dest => dest.Especialidad, fte => fte.MapFrom(src => src.Especialidad))
.ForMember(dest => dest.CostoAlCliente, fte => fte.MapFrom(src => (src.SalarioXHora * 0.40) + src.SalarioXHora));



Como podemos ver en el código anterior, se agrego una nueva propiedad al mapeo, pero en este caso, la expresión lambda se modificó para que haga el cálculo del CostoAlCliente de este empleado. Para entender más las expresiones lambda pueden leer este post que escribí tiempo atrás. Por último vamos a modificar el método para imprimir las entidades antes y después del mapeo.



EmpleadoOferta empleadoOferta1 = Mapper.Map<Empleado, EmpleadoOferta>(empleado1);

Console.WriteLine("----- Empleado para la Oferta -----");
Console.WriteLine("Id Empleado {0}", empleadoOferta1.Id);
Console.WriteLine("Nombre {0}", empleadoOferta1.Nombre);
Console.WriteLine("Experiencia del Empleado {0}", empleadoOferta1.AnnosExperiencia);
Console.WriteLine("Especialidad del Empleado {0}", empleadoOferta1.Especialidad);
Console.WriteLine("Costo al cliente {0}", empleadoOferta1.CostoAlCliente);



Al ejecutar el código modificado el resultado obtenido es el siguiente:



image



Como podemos ver en la figura anterior, se puede ver el cáculo hecho en el mapeo al calcular a partir del salario por hora un sobre precio del 40%.





Technorati Tags: ,,

8.02.2010

Presentando Imágenes y Texto en un ComboBox en WPF

Cuando realizamos proyectos en WFP, es común tener que hacer que la aplicación sea lo más visual posible para el usuario final, esto por que cuando una empresa decide moverse de windows Forms a WPF normalmente lo hace por que quiere aprovecharse de todas las bondades visuales de XAML y WPF para hacer lucir sus aplicaciones espectaculares. En este post vamos a ver como desplegar una imagen dentro de un combo box, y además le vamos a agregar una descripción a esta imagen en el mismo control.

Detalle de la Solución

En este caso vamos a crear una pantalla muy simple en donde vamos a pone un combo box, para que el usuario seleccione el empleado que desea. Al mismo tiempo vamos a desplegar el nombre y los apellidos del empleado seleccionado en la parte inferior del combo box.

Solución #1: Imágenes en el Path

La primera solución que vamos a plantear es tener las imágenes un directorio en el disco duro de la máquina, y en el registro del empleado, vamos a poner el nombre de la imagen que posee. Esta solución es la mas simple sin embargo tiene sus contras, por ejemplo con esta opción es imposible garantizar la integridad referencial de los datos, ya que si se elimina el registro no se puede garantizar el eliminado de la foto en el directorio – entre otras cosas.

Para iniciar, vamos a conseguir las imagenes de los empleados que vamos a tener disponibles en el combo, yo en mi caso busque imagenes de caricatura en Bing->Images y las agregue a un directorio que hice en mi proyecto para tener las imagenes disponibles. La siguiente imágen muestra el agregado de las fotos del usuario y su ubicación en el proyecto

image

image

Seguidamente procedemos a crear la base de datos donde vamos a guardar los datos del empleado, para este le damos al proyecto agregar nuevo ítem y procedemos a agregar una base de datos local la cual llamaremos PeopleDB.sdf como se ve en la siguiente imagen.

image

Luego creamos una tabla en la base de datos utilizando el Database Explorer de VS2010 – funciona igual en VS2008 -

image

En la tabla vamos a crear los siguientes campos.

image

Ahora procedemos a agregarle campos de forma manual utilizando el database explorer en la opción “Show Table Data”. Como podemos ver, hemos puesto la ruta de las fotos como “Images\<Nombre Foto>”

image

Seguidamente vamos a crear una clase que me permita representar cada uno de los empleados con los que voy a interactuar, para así poder crear una clase de lógica de negocios que me permita obtener los datos del empleado. – en este caso estamos creando la clase de entidades, de acceso a datos y de negocios en el mismo proyecto, pero como se puede leer en posts anteriores, lo recomendable es tener librerías para cada una de estas capas.

La capa de entidades tiene cada una de las propiedades automáticas para almacenar cada uno de los valores de los campos de la tabla especificada enteriormente.

image

Seguidamente procedemos a construir la clase para el acceso a datos. Esta clase se presenta  a continuación

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlServerCe;
using System.Data;

namespace WPFTricks
{
public static class Empleado_DAL
{

static string dbFile = @"Data Source=PeopleDB.sdf";

public static List<Empleado> ObtenerEmpleados()
{
List<Empleado> empleados = new List<Empleado>();
try
{
using (SqlCeConnection connection = new SqlCeConnection(dbFile))
{
SqlCeCommand command = new SqlCeCommand("SELECT * FROM Empleados", connection);
connection.Open();
IDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Empleado emp = new Empleado();

emp.Id = System.Convert.ToInt32(reader["Id"]);
emp.Nombre = reader["Nombre"].ToString();
emp.Apellido1 = reader["Apellido1"].ToString();
emp.Apellido2 = reader["Apellido2"].ToString();
emp.Direccion = reader["Direccion"].ToString();
emp.Email = reader["Email"].ToString();
emp.FotoUrl = reader["Imagen"].ToString();

empleados.Add(emp);
}
}
}
catch (Exception ex)
{
throw ex;
}
return empleados;
}
}
}



Como se puede ver en el código anterior, tenemos un método que devuelve todos los empleados almacenados en la tabla empleados. Algo a señalar es que el connection string utilizado es de la forma static string dbFile = @"Data Source=PeopleDB.sdf";  el cual es de esta forma, porque nuestra base de datos tiene modificada la propiedad “Copy to Output Directory” tal y como se ve en la siguiente figura. En este caso el valor de esta propiedad ha sido cambiado a “Copy Always” con lo cual la base de datos se copia al directorio de salida de compilación, por lo que la base de datos residirá en el mismo directorio que el ejecutable y las librerías de nuestra aplicación demo.



image



Ahora el siguiente paso es crear la lógica de negocios, la cual se muestra a continuación:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WPFTricks
{
public class Empleado_BL
{
public List<Empleado> ObtenerEmpleados()
{
return Empleado_DAL.ObtenerEmpleados();
}
}
}



Esta clase es bastante simple, ya que por la naturaleza de nuestro demo, el único método que vamos a tener en la clase es el que retorna la lista de empleados desde la capa de acceso a datos, y al ser los métodos del DAL estáticos, lo único que tenemos que hacer es invocar y retornar el resultado de la invocación del método ObtenerEmpleados.



A nivel de UI si vamos a tener un combobox en donde vamos a desplegar el nombre y la foto de nuestro empleado. En modo diseño así es como luce nuestra pantalla:



image



El código para cargar el ComboBox es bastante simple y ya lo hemos visto en varios post anteriores de WPF, por lo que esta vez lo vamos a poner pero no lo vamos a comentar:



public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}


private void Window_Loaded(object sender, RoutedEventArgs e)
{
Empleado_BL empleado = new Empleado_BL();
List<Empleado> lista = empleado.ObtenerEmpleados();
cmbEmpleados.ItemsSource = lista;
}
}



El código donde esta el truco es en el código XAML. El código se puede ver a continuación:



<Window x:Class="WPFTricks.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="383" Width="659" Loaded="Window_Loaded">
<
Grid>
<
ComboBox ItemsSource="{Binding}" Height="40" HorizontalAlignment="Left" Margin="56,49,0,0" Name="cmbEmpleados" VerticalAlignment="Top" Width="215" >
<
ComboBox.ItemTemplate>
<
DataTemplate>
<
Grid>
<
Grid.ColumnDefinitions>
<
ColumnDefinition></ColumnDefinition>
<
ColumnDefinition></ColumnDefinition>
</
Grid.ColumnDefinitions>
<
Label Content="{Binding Path=Nombre}" Grid.Column="1" Height="28" HorizontalAlignment="Left" Margin="10,10,0,0" Name="label1" VerticalAlignment="Top" />
<
Image Source="{Binding Path=FotoUrl}" Grid.Column="0" Height="40" Width="40" Stretch="Fill"></Image>
</
Grid>
</
DataTemplate>
</
ComboBox.ItemTemplate>
</
ComboBox>

</
Grid>
</
Window>



En este caso, lo importante esta en el ItemTemplate. Esta clase nos permite personalizar la manera en que se van a desplegar los ítems en el combo. El Ítem template permite asignar un DataTemplate, el cual describe la estructura visual de un elemento dentro de un control en WPF, esto cuando se hace una operación de binding a un ItemsSource. En este caso, vamos a agregar un Grid en el DataTemplate, luego vamos a agregar dos columnas por cada ítem, en la primera columna vamos a poner la foto, y en la segunda columna el nombre del empleado; para lograr esto definimos dentro del DataTemplate los controles antes mencionados y procedemos a indicar en cual columna se deben de ubicar utilizando el propiedad Grid.Column. Por último hacemos el binding directo a los campos de la clase, por lo que aunque los campos tenga otro nombre en la tabla, lo importante en este caso es el nombre de las propiedades que lo representan en el objeto, en nuestro caso, FotoUrl y Nombre. El resultado al ejecutar el demo anterior es el siguiente:



image



Si seleccionamos uno de los elementos, el ítem seleccionado se ve de la siguiente manera:



image



Technorati Tags: ,,