[LINQ]El método ToLookUp

Definición

Según la definición de MSDN, el método ToLookUp ejecutado sobre una variable que implemente el Tipo IEnumarable<T>, va a devolver un Diccionario (Dictionary) a través de las condiciones de agrupación indicadas, y mostrando el dato indicado.

Ejemplos de Uso

Para ver la utilidad de este método, vamos a ver una serie de ejemplos basados en una lista de Ejemplo acerca de la siguiente clase:

   1: class Persona

   2: {

   3:     public string Nombre { get; set; }

   4:     public int Edad { get; set; }

   5:     public string Departamento { get; set; }        

   6: }

Y una lista con los siguientes datos:

   1: personas.Add(new Persona { Nombre = "Alvaro", Edad = 1, Departamento = "Habitacion 1" });

   2: personas.Add(new Persona { Nombre = "Izan", Edad = 1, Departamento = "Habitacion 1" });

   3: personas.Add(new Persona { Nombre = "Martina", Edad = 29, Departamento = "Habitacion 2" });

   4: personas.Add(new Persona { Nombre = "Javi", Edad = 28, Departamento = "Habitacion 2" });

Ejemplo 1: Agrupar las personas según el Departamento al que pertenecen:

   1: var agrupados = personas.ToLookup(a => a.Departamento );

   2: foreach (var grupo in agrupados)

   3: {

   4:     Console.WriteLine(grupo.Key);

   5:     foreach (var persona in grupo)

   6:     {

   7:         Console.WriteLine(String.Format("{0} tiene {1} años",persona.Nombre,persona.Edad));

   8:     }

   9: }

El resultado obtenido sería:

   1: Habitacion 1 

   2: Alvaro tiene 1 años

   3: Izan tiene 1 años

   4: Habitacion 2

   5: Martina tiene 29 años

   6: Javi tiene 28 años

Ejemplo 2: Agrupar las personas según departamento y solo visualizar su nombre.

   1: var agrupados = personas.ToLookup(a => a.Departamento,a=>a.Nombre );

   2: foreach (var grupo in agrupados)

   3: {

   4:     

   5:     Console.WriteLine(grupo.Key);

   6:     foreach (var persona in grupo)

   7:     {

   8:         Console.WriteLine(String.Format("Nombre: {0}",persona));

   9:     }

  10: }

 

Cuyo resultado sería:

   1: Habitacion 1

   2: Nombre: Alvaro

   3: Nombre: Izan

   4: Habitacion 2

   5: Nombre: Martina

   6: Nombre: Javi

 

Ejemplo 3: Agrupar las personas por departamento y edad:

   1: var agrupados = personas.ToLookup(a => new {a.Departamento,a.Edad});

   2: foreach (var grupo in agrupados)

   3: {

   4:     Console.WriteLine(String.Format("Departamento {0} Edad {1}",grupo.Key.Departamento,grupo.Key.Edad));

   5:     foreach (var persona in grupo)

   6:     {

   7:         Console.WriteLine(String.Format("Nombre: {0}",persona.Nombre));

   8:     }

   9: }

 

Y el resultado sería:

   1: Departamento Habitacion 1 Edad 1

   2: Nombre: Alvaro

   3: Nombre: Izan

   4: Departamento Habitacion 2 Edad 29

   5: Nombre: Martina

   6: Departamento Habitacion 2 Edad 28

   7: Nombre: Javi

Bueno, espero que os sea de utilidad.

Saludos.

Patrón UoW Parte 2

Introducción

En vista de que parece que el artículo que publique ayer sobre UoW, parecía que era demasiado simple…, y a pesar de que Lucas Ontivero ha publicado un ejemplo (Gracias), he decidido publicar un ejemplo un poco más amplio para ver su utilización.

Usando el Patrón

Partiendo de una clase base:

   1: public abstract class cBase : INotifyPropertyChanged

   2:     {

   3:         public event PropertyChangedEventHandler PropertyChanged;

   4:         public void CambiarPropiedad(string Propiedad)

   5:         {

   6:             if (PropertyChanged != null)

   7:             {

   8:                 PropertyChanged(this, new PropertyChangedEventArgs(Propiedad));

   9:             }

  10:         }

  11:         public abstract SqlCommand Insertar();

  12:         public abstract SqlCommand Modificar();

  13:         public abstract SqlCommand Eliminar();

  14:     }

La clase base va a servir como definición de nuestras entidades, va a incluir el evento que va a permitir tener un control sobre los cambios, y va a declarar como abstractos las funciones de Insertar, Modificar y Eliminar que utilizaremos un poco más adelante.

Para el ejemplo he creado una pequeña Base de Datos en SQL Server que presenta una tabla muy sencilla:

 

Para “mapear” los datos de está tabla voy a hacer uso de la clase Persona:

 

   1: public class Persona : cBase

   2:     {

   3:         private int _ID = 0;

   4:         public int ID

   5:         {

   6:             get 

   7:             { 

   8:                 return _ID; 

   9:             }

  10:             set 

  11:             {

  12:                 if (!_ID.Equals(value))

  13:                 {

  14:                     _ID = value;

  15:                     CambiarPropiedad("ID");

  16:                 }

  17:             }

  18:         }

  19:         private string _Nombre = string.Empty;

  20:         public string Nombre 

  21:         {

  22:             get

  23:             {

  24:                 return _Nombre; 

  25:             }

  26:             set

  27:             {

  28:                 if (!_Nombre.Equals(value))

  29:                 {

  30:                     _Nombre = value;

  31:                     CambiarPropiedad("Nombre");

  32:                 }

  33:             }

  34:         }

  35:         private DateTime _Fecha = DateTime.Now;

  36:         public DateTime Fecha

  37:         {

  38:             get

  39:             {

  40:                 return _Fecha;

  41:             }

  42:             set

  43:             {

  44:                 if (!_Fecha.Equals(value))

  45:                 {

  46:                     _Fecha = value;

  47:                     CambiarPropiedad("Fecha");

  48:                 }

  49:             }

  50:         }

  51:         public override SqlCommand Insertar()

  52:         {

  53:             SqlCommand command = new SqlCommand("INSERT INTO PERSONA (NOMBRE,FECHA) VALUES(@NOMBRE,@FECHA)");

  54:             command.Parameters.AddWithValue("Nombre", Nombre);

  55:             command.Parameters.AddWithValue("Fecha", Fecha);

  56:             return command;

  57:         }

  58:         public override SqlCommand Modificar()

  59:         {

  60:             SqlCommand command = new SqlCommand("UPDATE PERSONA SET NOMBRE=@NOMBRE, FECHA=@FECHA WHERE ID=@ID");

  61:             command.Parameters.AddWithValue("ID", ID);

  62:             command.Parameters.AddWithValue("Nombre", Nombre);

  63:             command.Parameters.AddWithValue("Fecha", Fecha);

  64:             return command;

  65:         }

  66:         public override SqlCommand Eliminar()

  67:         {

  68:             SqlCommand command = new SqlCommand("DELETE FROM PERSONA WHERE ID=@ID");

  69:  

  70:             command.Parameters.AddWithValue("ID", ID);

  71:             return command;

  72:         }

  73:         public static List<Persona> Obtener()

  74:         {

  75:             List<Persona> listado = new List<Persona>();

  76:             DataTable datos = new DataTable();

  77:             using (SqlConnection con = new SqlConnection(@"Data Source=.SQLEXPRESS;Initial Catalog=Concept;Integrated Security=True;Pooling=False"))

  78:             {

  79:                 using (SqlDataAdapter da = new SqlDataAdapter("SELECT ID,NOMBRE,FECHA FROM PERSONA", con))

  80:                 {

  81:                     da.Fill(datos);

  82:                 }

  83:             }

  84:             foreach (DataRow row in datos.Rows)

  85:             {

  86:                 listado.Add(new Persona () { ID=Convert.ToInt32(row["ID"]),

  87:                                              Nombre = Convert.ToString(row["Nombre"]),

  88:                                              Fecha = Convert.ToDateTime(row["Fecha"])

  89:                 });

  90:             }

  91:             return listado;

  92:         }

  93:     }

En estás clase hago la implementación de los métodos abstractos que había definido en la clase Base y que van a construir objetos de tipo SqlCommand para que posteriormente sean tratados.

 

A continuación voy a exponer una “aproximación” más elaborada que la que expuse ayer, pero que tampoco es la que realmente utilizo cuando hago uso de este patrón.

 

   1: public class UnidadDeTrabajo

   2:     {

   3:        

   4:         List<cBase> objetosNuevos;

   5:         List<cBase> objetosModificados;

   6:         List<cBase> objetosEliminados;

   7:  

   8:         public UnidadDeTrabajo()

   9:         {

  10:             objetosNuevos = new List<cBase>();

  11:             objetosModificados = new List<cBase>();

  12:             objetosEliminados = new List<cBase>();

  13:        }

  14:  

  15:         public void Añadir(cBase objNuevo)

  16:         {

  17:             if (!objetosNuevos.Contains(objNuevo))

  18:             {

  19:                 objetosNuevos.Add(objNuevo);

  20:             }

  21:         }

  22:  

  23:         public void Eliminar(cBase objEliminado)

  24:         {

  25:             if (!objetosEliminados.Contains(objEliminado))

  26:             {

  27:                 objetosEliminados.Add(objEliminado);

  28:             }

  29:         }

  30:  

  31:         public void Modificar(cBase objModificado)

  32:         {

  33:             if (!objetosModificados.Contains(objModificado))

  34:             {

  35:                 objetosModificados.Add(objModificado);

  36:             }

  37:         }

  38:         public void Confirmar()

  39:         {

  40:             SqlCommand cmd;

  41:             using (TransactionScope scope = new TransactionScope())

  42:             {

  43:                 using (SqlConnection con = new SqlConnection(@"Data Source=.SQLEXPRESS;Initial Catalog=Concept;Integrated Security=True;Pooling=False"))

  44:                 {

  45:                     con.Open();

  46:                     foreach (cBase obj in objetosNuevos)

  47:                     {

  48:                         cmd = obj.Insertar();

  49:                         cmd.Connection = con;

  50:                         cmd.ExecuteNonQuery();

  51:                     }

  52:                     foreach (cBase obj in objetosModificados)

  53:                     {

  54:                         cmd = obj.Modificar();

  55:                         cmd.Connection = con;

  56:                         cmd.ExecuteNonQuery();

  57:                     }

  58:                     foreach (cBase obj in objetosEliminados)

  59:                     {

  60:                         cmd = obj.Eliminar();

  61:                         cmd.Connection = con;

  62:                         cmd.ExecuteNonQuery();

  63:                     }

  64:                     

  65:                 }

  66:                 scope.Complete();

  67:                 Limpiar();

  68:             }

  69:         }

  70:  

  71:         private void Limpiar()

  72:         {

  73:             objetosNuevos.Clear();

  74:             objetosModificados.Clear();

  75:             objetosEliminados.Clear();

  76:         }

  77:     }

La función que realmente interesa de la clase UnidadDeTrabajo es Confirmar. Como podéis observar “solo” se declara una conexión, y “solo” se abre una vez, para ejecutar todos los comandos existentes en las listas de objetos nuevos, objetos modificados y objetos eliminados.

Para ver un pequeño ejemplo de su uso:

   1: var personas = Persona.Obtener();

   2:            

   3:            UnidadDeTrabajo unit = new UnidadDeTrabajo();

   4:            unit.Eliminar(personas.Where(n=>n.Nombre.ToUpper().Equals("PRUEBA")).FirstOrDefault());

   5:            personas.Where(n => n.Nombre == "Javier").FirstOrDefault().Nombre = "JTP";

   6:            unit.Modificar(personas.Where(n => n.Nombre.ToUpper().Equals("JTP")).FirstOrDefault());

   7:            unit.Añadir(new Persona() { Nombre = "Test", Fecha = DateTime.Now });

   8:            unit.Confirmar();

 

En respuesta a los comentarios del anterior post

Creo que este ejemplo deja claro, el tener un conjunto de datos que se van a tratar de una “tacada” a la hora de ser enviados a la base de datos.

Con respecto a las relaciones, independientemente que usemos EF o no, tendremos que tener los métodos adecuados para tratar dichas relaciones si es que se realizan desde la aplicación, quizás existen triggers para manejar dichos aspectos (aunque no sea una cosa que me gusten en exceso, puede resultar fácil).

 

Bueno espero que este Post os guste más que el anterior.

 

Saludos

 

Patrón Unit of Work (UoW) o Unidad de Trabajo

Definición

Este patrón tiene como objetivo tratar como una Unidad todos aquellos objetos nuevos, modificados o eliminados con respecto de una fuente de datos.

Martin Fowler, ya realizó una descripción de UoW en su libro “Patterns of Enterprise Application Architecture”.

Aproximación

Una aproximación, similar a la que usa Fowler para definir el patrón, podría ser:

   1: public class UnidadDeTrabajo

   2:     {

   3:        

   4:         List<cBase> objetosNuevos;

   5:         List<cBase> objetosModificados;

   6:         List<cBase> objetosEliminados;

   7:  

   8:         public UnidadDeTrabajo()

   9:         {

  10:             objetosNuevos = new List<cBase>();

  11:             objetosModificados = new List<cBase>();

  12:             objetosEliminados = new List<cBase>();

  13:        }

  14:  

  15:         public void Añadir(cBase newObj)

  16:         {

  17:  

  18:         }

  19:  

  20:         public void Eliminar(cBase delObj)

  21:         {

  22:          

  23:         }

  24:  

  25:         public void Modificar(cBase modObj)

  26:         {

  27:             

  28:         }

  29:         public void Confirmar()

  30:         {

  31:             

  32:         }

  33:  

  34:         private void Limpiar()

  35:         {

  36:             objetosNuevos.Clear();

  37:             objetosModificados.Clear();

  38:             objetosEliminados.Clear();

  39:         }

  40:     }

Dentro de la definición de la clase podemos encontrar lo siguiente:

– Declaración de las listas que van a contener los objetos Nuevos, Modificados y Eliminados.

– Métodos para agregar los elementos Nuevos, los elementos modificados y los que serán eliminados.

– Método “Confirmar” que será el encargado de enviar los cambios a la Base de Datos.

– Método limpiar para vaciar las listas.

 

Las definiciones de los métodos están vacías, falta la lógica donde se va a comprobar si se deben o no añadir a la lista indicada el objeto deseado.

En el ejemplo estoy utilizando el tipo “cBase” que digamos es la clase base de cada una de las entidades de mi negocio.

A la hora de Confirmar los cambios, se podrían realizar las comprobaciones pertinentes para determinar que los datos no han sido modificados o eliminados por otro Usuario.

 

Conclusiones

El patrón UoW nos va a resultar muy útil a la hora de persistir un conjunto de acciones a ejecutar sobre la base de datos, evitando el exceso de conexiones contra la misma.

Se puede utilizar con la potencia de Entity Framework, por lo que es algo muy a tener en cuenta.

 

Saludos!

[How To] Transponer un Objeto DataTable con LINQ

Introducción

Una pregunta bastante utilizada en foros MSDN es como Transponer un DataTable al igual que se puede hacer en Excel.

¿Qué es Transponer una Tabla?

A simple vista será cambiar Columnas por filas.

Podríamos obtener los datos modificados desde la base de datos, con algún quebradero de cabeza. Pero, me he animado a crear un método para ayudar en esta tarea.

 

Métodos Usados

Bueno, como ya he comentado alguna vez LINQ es algo que me encanta, así que me he decantado a crear un “método extensor” y dos métodos auxiliares en C# y usando LINQ.

En primer lugar, vamos a determinar cuales serán las columnas de la nueva tabla. Para ello, como indicaba antes, las filas originales van a pasar a ser columnas, por lo tanto tantas filas originales, tantas columnas nuevas vamos a tener. Además de ello, agregaremos una nueva columna que será la cabecera de nuestra columna antigua.

   1: private static void CreatingColumns(DataTable dtOld, DataTable dtNew)

   2:        {

   3:            DataColumn[] arr = new DataColumn[] { new DataColumn("Head") }

   4:                .ToList()

   5:                .Union<DataColumn>(dtOld.Rows.Cast<DataRow>()

   6:                    .ToList()

   7:                    .Select(row => new DataColumn(Convert.ToString(row[0])))

   8:                    ).ToArray();

   9:  

  10:            dtNew.Columns.AddRange(arr);

  11:  

  12:        }

Una vez que hemos definido nuestras columnas, vamos a agregar las nuevas filas:

   1: private static void CreatingRows(DataTable dtOld, DataTable dtNew)

   2:         {

   3:             dtOld.Columns.Cast<DataColumn>()

   4:                 .ToList()

   5:                 .ForEach(a =>

   6:                     dtNew.Rows.Add(new string[] { a.ColumnName }.Union(

   7:                         dtOld.Rows.Cast<DataRow>()

   8:                         .ToList()

   9:                         .Select(row => row[a.ColumnName])).ToArray()));

  10:  

  11:  

  12:         }

Y por ultimo vamos a crear el método extensor:

   1: public static DataTable Transpond(this DataTable dtOld)

   2:        {

   3:            DataTable dtNew = new DataTable(dtOld.TableName);

   4:  

   5:            Extensions.CreatingColumns(dtOld, dtNew);

   6:  

   7:            Extensions.CreatingRows(dtOld, dtNew);

   8:  

   9:            return dtNew;

  10:        }

 

Bueno, espero que os guste y os sea de utilidad.

He publicado el código fuente aquí.

Saludos!

[EF + ORACLE] Procedimientos almacenados EF 4.1 y la Beta 2 de Oracle

Introducción

Hace un tiempo realice una serie de post acerca de usar Oracle con EF, y hace un mes os hable de la salida de la Beta 2 del Driver de Oracle para EF.

En este post vamos a ver como utilizar Procedimientos Almacenados de Oracle desde Entity Framework.

Usando Procedimientos

Partiendo del procedimiento:

   1: PROCEDURE "PROCEDURE1" (

   2:   "COCHE" OUT  SYS_RefCursor) IS

   3: BEGIN 

   4: open COCHE FOR SELECT id_coche,nombre,marca FROM COCHES;

   5:  

   6: END;

Donde vamos a devolver un cursor con toda la información de la Tabla Coches. El procedimiento compila y funciona de forma correcta.

Para poder hacer funcionar el procedimiento, y antes de agregarlo a nuestro modelo, será necesario realizar los siguientes pasos:

1) Agregar la referencia a “Oracle.DataAccess.Client”.

2) Configurar el fichero de Configuración para definir el Cursor:

Dentro del fichero de configuración vamos a definir una sección “oracle.dataaccess.client”:

   1: <oracle.dataaccess.client>

   2:   <settings>

   3:     <add name="JTorrecilla.Procedure1.RefCursor.Coche" value="implicitRefCursor bindinfo='mode=Output'" />

   4:     <add name="JTorrecilla.Procedure1.RefCursorMetaData.Coche.0" 

   5:     value="implicitRefCursor metadata='ColumnName=ID_COCHE;BaseColumnName=ID_COCHE; 

   6:            BaseTableName=COCHES;NATIVE_DATA_TYPE=number;ProviderType=Decimal;PROVIDER_DB_TYPE=Decimal; 

   7:            DataType=System.Decimal;ColumnSize=8;AllowDbNull=False'" />

   8:     <add name="JTorrecilla.Procedure1.RefCursorMetaData.Coche.1" 

   9:             value="implicitRefCursor metadata='ColumnName=NOMBRE;BaseColumnName=NOMBRE;

  10:              BaseTableName=COCHES;NATIVE_DATA_TYPE=Varchar2;ProviderType=Varchar2;

  11:              DataType=System.String;ColumnSize=50'" />

  12:     <add name="JTorrecilla.Procedure1.RefCursorMetaData.Coche.2" 

  13:              value="implicitRefCursor metadata='ColumnName=MARCA;

  14:              BaseColumnName=MARCA;BaseTableName=COCHES;NATIVE_DATA_TYPE=Varchar2;

  15:              ProviderType=Varchar2;DataType=System.String;ColumnSize=50'" />

  16:   </settings>

  17: </oracle.dataaccess.client>

Hemos añadido 4 lineas: la primera define el nombre del cursor y el modo de enlace. Las otras entradas, definen cada una de las columnas que se utilizan en el cursor, así como los tipos de datos y su mapeo dentro de .NET.

Las características que se pueden definir son:

– Nombre de la Columna (ColumnName)

– Tipo de datos nativo en Oracle (Native_Data_Type)

– Tipo de datos del proveedor Oracle (ProviderType)

– Tipo de datos de BD del proveedor(ProviderDBType)

-Tipo de Dato soportado por .NET (DataType)

– Tamaño (ColumnSize)

– Precision (NumericPrecision) y Escala (NumericScale)

– Admisión de Nulos (AllowDBNull)

Una vez configurado el Cursor, vamos a importarlo dentro de nuestro modelo de datos en EF:

Abriremos el explorador del modelo, y desplegaremos los Procedimientos almacenados:

Con el botón secundario sobre “Procedure1” mostraremos el menú contextual y seleccionaremos la opción “Add Function Import”.

Con ello se abrirá un asistente donde elegiremos el Procedimiento que queremos importar, podremos indicar como querremos llamar a la función dentro de nuestro código y nos permitirá ver la información que hemos definido en el fichero de configuración tras presionar el botón “Get Column Info”

En este ejemplo, en el que el resultado del procedimiento devuelve un tipo “Coches” podemos definir que el procedimiento devuelve una colección de ese tipo.

En el caso de que el resultado no se corresponda con una de nuestras entidades, tenemos la posibilidad de definir un nuevo tipo de datos complejo a través del botón “Create New Complex Type”.

Para consumir este procedimiento bastaría con ejecutar el siguiente código:

 

   1: var coches = ent.PROCEDURE1();

   2: foreach (var c in coches)

   3:                 MessageBox.Show(c.MARCA);

 

Espero que os sea de utilidad.

Bibliografía:

Readme Beta 2 de Oracle

[CR] Usando Listas para llenar nuestros informes (II).

Introducción

En el post anterior hemos visto como definir un informe en base a una clase propia y enlazarlo a una lista genérica desde VS.

Otra característica que se añadio con VS 2005 y que hoy en día es bastante utilizada, son los tipos de datos “Nullables” o la versión de El Guille.

A la hora de definir una clase que tenga este tipo de datos, nos vamos a encontrar con un problema a la hora de enlazarlo al informe en tiempo de ejecución:

 

 

¿Cómo Solucionarlo?

Como normalmente digo, esto es una aproximación y no creo que sea la mejor. Como también comente en el post anterior el ejemplo lo he basado en VS2005.

Partiendo de la clase cPersona donde la propiedad ID es de tipo Nullabe of Int:

 

   1: public class cPersona

   2:  {

   3:      public cPersona()

   4:      {

   5:  

   6:      }

   7:      private int? _ID;

   8:  

   9:      public int? ID

  10:      {

  11:          get { return _ID; }

  12:          set { _ID = value; }

  13:      }

  14:      private string _Nombre;

  15:  

  16:      public string Nombre

  17:      {

  18:          get { return _Nombre; }

  19:          set { _Nombre = value; }

  20:      }

  21:  }

Vamos a definir una serie de funciones que van a modificar los datos para evitar el problema anterior:

La función de este fragmento va a devolver el tipo de dato Original a través de Expresiones Regulares.

   1: public static Type ObtenerTipo(string Dato)

   2: {

   3:     string valores="System.String";

   4:     try

   5:     {

   6:         valores = Regex.Split(Dato, @"(d*[|s*])")[2];   

   7:     }

   8:     catch (Exception)

   9:     {

  10:  

  11:         valores = "System.String";

  12:     }

  13:     return Type.GetType(valores);

  14: }

Y la función importante va a encargarse de definir un objeto de Tipo DataTable que posteriormente enlazaremos a nuestro informe:

   1: public static DataTable GetDataTableNotNullable(List<cPersona> listado)

   2:         {

   3:             DataTable dt = new DataTable();

   4:             DataColumn col;

   5:             DataRow row;

   6:             Object val;

   7:             foreach (PropertyInfo prop in listado[0].GetType().GetProperties())

   8:             {

   9:                 col = new DataColumn(prop.Name);

  10:                 if (!prop.PropertyType.ToString().Contains("Nullable"))

  11:                     col.DataType=prop.PropertyType;

  12:                 else

  13:                     col.DataType = ObtenerTipo(prop.ToString());

  14:                 dt.Columns.Add(col);

  15:             }

  16:             

  17:             foreach (cPersona persona in listado)

  18:             {

  19:                 row = dt.NewRow();

  20:                 foreach (PropertyInfo prop in persona.GetType().GetProperties())

  21:                 {

  22:                     val = prop.GetValue(persona, null);

  23:                     if (val == null) val = DBNull.Value;

  24:                     row[prop.Name] = val;

  25:                 }

  26:                 dt.Rows.Add(row);

  27:             }

  28:             return dt;

  29:         }

 

Os dejo el enlace al proyecto para que le deis un vistazo.

 

Bueno espero que os sea de utilidad.

 

Saludos!

 

PD: Se podría hacer con métodos extensores y quedaría muy bien :-D!

[CR] Usando Listas para llenar nuestros informes (I).

Introducción

Un tema muy común a cualquier aplicación y una de las cuestiones más preguntadas en los foros de MSDN, es acerca de los informes con Crystal Reports.

Hasta la llegada de Visual Studio 2005, la forma más sencilla de rellenar con datos y definir un informe se realizaba gracias a los DataSet Tipados.

Con Visual Studio 2005 se introdujeron las Listas (List) que son colecciones de datos. También se modificaron los asistentes de los Informes de Crystal Reports para admitir definir un informe basado en una clase.

En este primer post vamos a ver como mostrar un informe de Crystal Reports utilizando como fuente de datos una Lista de una clase propia.

Creando un Ejemplo

Para este pequeño ejemplo introductorio, voy a utilizar Visual Studio 2005, y quiero recalcar que es válido en VS2008 y VS2010.

En primer lugar, vamos a definir la clase base de nuestro informe, y una vez creada Genereramos la solución para que se refresquen los orígenes de datos:

   1: public class cPersona

   2:     {

   3:         public cPersona()

   4:         {

   5:  

   6:         }

   7:         private int _ID;

   8:  

   9:         public int ID

  10:         {

  11:             get { return _ID; }

  12:             set { _ID = value; }

  13:         }

  14:         private string _Nombre;

  15:  

  16:         public string Nombre

  17:         {

  18:             get { return _Nombre; }

  19:             set { _Nombre = value; }

  20:         }

  21:     }

El siguiente paso, será crear un nuevo Informe:

a) Agregaremos un nuevo fichero de tipo Informe Crystal Reports a Nuestro Proyecto.

b) Desde el asistente, indicaremos que vamos a usar el asistente y un informe standard:

c) Seleccionaremos el Origen de Datos dentro de los Objetos de .Net:

d) Indicaremos que propiedades de la clase queremos mostrar en el informe:

e) Pulsaremos en Finalizar y aparecerá el diseñador del informe para que podamos personalizarlo a nuestro gusto:

 

Una vez definido el informe, vamos a ver como asginar el origen de datos y mostrarlo en un control Report Viewer:

   1: InformePersonal informe = new InformePersonal();

   2: List<cPersona> personas = new List<cPersona>();

   3: cPersona persona = new cPersona();

   4: for (int i = 0; i < 15; i++)

   5: {

   6:     persona = new cPersona();

   7:     persona.ID = i;

   8:     persona.Nombre = String.Format("Nombre {0}", i);

   9:     personas.Add(persona);

  10: }

  11: crystalReportViewer1.ReportSource = informe;

  12: informe.SetDataSource(personas);

Conclusión

Creo que el poder enlazar como tipo de origen una clase propia en lugar de un DataSet tipado, nos va a permitir trabajar de forma más sencilla y rápida.

 

Espero que os sea de utilidad.

 

Saludos!