Saturday, January 20, 2007
.NET News y algo más
Para los seguidores de desarrollador 5 Estrellas 2005, ya está la 4ta estrella disponible, con muy buen material acerca
de los temás relevantes del .NET Framwork 3.0
http://www.mslatam.com/latam/msdn/comunidad/dce2005/
Algo que me ha llamado la tención y habia estado esperando es el show para televisión de MSDN latinoamérica. Con temas
de bastante interés es una opción más de aprendizaje a la que tenemos acceso.
http://www.microsoft.com/spanish/msdn/latam/mediacenter/msdntv/default.aspx
Imagina un mundo donde la tecnología facilite el acceso a la educación a todo el mundo...Este es el lema para el Imagine Cup 2007, ya ya está listo para que los estudiantes de todo el mundo se inscriban y participen en el evento tecnológico a nivel de Universidades que Microsoft realiza cada año.
El sitio recomendado para hoy es: http://www.theserverside.net/, uno de mis sitios favoritos por las grandiosas entradas que hacen expertos en los diferentes temas de la tecnología .NET, además de poder conseguir apartes de libros, artículos, conversatorios, y mucho más.
Más TIPS sobre Crystal Reports
1. Supongamos que tenemos un reporte el cual contiene varios subreportes. Nuestro reporte principal tiene la orientación del papel de manera vertical, pero uno de los subreportes es demasiado ancho y necesita estar orientado de manera horizontal. Qué se puede hacer en este caso? Aqui está el código y el evento que debemos programar del CrystalReportViewer para alcanzar esta solución:
private void CrystalReportViewer1_DrillDownSubreport(object source, CrystalDecisions.Web.DrillSubreportEventArgs e)
{
if(e.NewSubreportName == "Subreport1")
{
ReportePrincipal.PrintOptions.PaperOrientation = PaperOrientation.Landscape;
}
}
Capturando el evento DrillDownSubreport, le estamos indicando en ese instante al subreporte que tiene asignado en su propiedad "name" el valor Subreport1, que debe utilizar el valor "Landscape" para la orientación del papel. Esta solución es muy efectiva para alcanzar el resultado esperado, además que podemos hacerlo con 1 o más subreportes adicionados al principal.
2. Cunado necesitamos agrupar información dentro de nuestro reporte, y mostrar cada grupo en una página diferente del reporte, Crystal ofrece un solución bastante simple, pero efectiva, que nos lleva a programar los eventos disparados por las diferentes secciones del reporte. En este caso vamos a programar el evento "New Page After" de las secciones GroupHeader y GroupFooter.
Lo primero entonces es indicarle al Group Header, que despligue la información del mismo grupo mientras no encuentre el último registro y el siguiente registro sea igual al que se está leyendo en el momento. Si no se cumple, entonces se ejecutaré el evento "New Page After", causando que se crea la nueva página para el nuevo grupo.
if(NOT OnLastRecord AND {sales.ord_num}=Next ({sales.ord_num}))then false
Pero esta solución causa un problema en el reporte y es que siempre se crea una página en blanco adicional, pero esto tiene una solución igualmente simple.
En la sección Group Footer, en el evento "New Page After" adicionamos la línea:
OnLastRecord
Con esto entonces tenemos dos simples soluciones a algo que en primera instancia parece ser un problema díficil de resolver. Como les decía en la entrada anterior sobre Crystal, esto es solo cuestión de conocer la herramienta y las capacidades que oferece a nível de programación.
Quiero recomendarles:
http://aspadvice.com/blogs/rjdudley/archive/tags/Crystal+Reports+.NET/default.aspx
http://www.crystalreportsbook.com/
Dos exelentes sites, de dos personajes que aportan bastante a dar soluciones efectivas a problemas con Crystal Reports utilizado desde nuestras aplicaciones .NET
SQLServer 2005 Administrado
Para crear un proyecto desde Visual Studio .NET 2005 para SQL Server, se escoge la opción SQL Server Project:
Esta template, nos permite un proyecto de SQL Server, desde el cual podremos adicionar los siguientes items:
Seleccionando cualquiera de los items anteriores, visual studio crea por nosotros la clase en la cual podemos adicionar el método, que finalmente será desplegado dentro del motor como un objeto de la base de datos que puede ejecutarse como cualquiera de sus contrapartes escritos en Transact SQL.
Por ejemplo, veamos lo que sería un método administrado que SQL Server 2005 entiende como un procedimiento almacenado, con los elementos necesarios para que funcione como tal:
using (SqlConnection conexion = new SqlConnection("context connection = true"))
{
SqlCommand comando = new SqlCommand();
comando.CommandText = "SELECT LastName" + Convert.ToChar("+").ToString() + "' '" +
Convert.ToChar("+").ToString() + "FirstName, Title, BirthDate, Address, City " +
"FROM dbo.Employees";
comando.Connection = conexion;
conexion.Open();
SqlContext.Pipe.ExecuteAndSend(comando);
}
Es una consulta muy sencilla, donde se pueden evidenciar algunos elementos de importancia como son:
1. Al constructor de la clase SqlConnection ahora le pasamos el valor "context connection = true" que está indicando que nuestro código se va a ejecutar dentro del contexto de SQL Server, administrado por el CLR. Esto significa que no van a ejecutarse todos los mecánismos de conexión utilizados por SQLConnection a través del SQLDataProvider.
2. El método Send de la propiedad Pipe es equivalente a un PRINT en Transact SQL. La propiedad Pipe es de tipo SQLPipe, y permite insertar información en la corriente de salida utilizando para esto el protocolo TDS(Tabular Data String), con el cual SQL Server habla con un cliente. TDS permite poner ResultSets y Mensajes en el camino.
Bien este método entonces será desplegado como un procedimiento almacenado dentro del motor. Y el despliegue? sencillo, simplemente ejecutar el proyecto y con esto ya tenemos desplegado nuestro procedimiento almacenado administrado como un objeto del motor. Hay comandos propios de Transact SQL que se pueden también ejecutar desde SQL Server 2005, pero eso es tema para otra entrada. Es importante que antes de ejecutar cualquiera de estos comandos, el CLR debe estar activado dentro de SQL Server, ya que por seguridad viene deshabilitado por defecto.
Desde visual studio .NET podemos hacer uso del Server Explorer para ir viendo lo que está ocurriendo con nuestros elementos administrados dentro del motor, ahi en diferentes carpetas podemos desplegar la de "Stored Procedures" y entonces el procedimiento almacenado tendrá el nombre que le hayamos dado al método escrito en C#. Otra carpeta que se encuentra es Assemblies, en la cual queda desplegado el ensamblado el cual toma por defecto el nombre de nuestro proyecto.
En conclusión, con SQL Server - CLR, tenemos muchas ventajas como por ejemplo: poder extender la funcionalidad del motor, porque podemos escribir nuestra lógica utilizando lenguajes de alto nivel y no Transact SQL. Aunque SQL Server no sea un motor orientado a objetos, estas características le añaden orientación a objetos de cierto modo, pero debemos tener cuidado, ya que ciertas características de la POO, no pueden ser entendidas dentro del SQL Server 2005. Es muy importante saber que no puedo tomar todos mis objetos escritos en Transact-SQL y cambiarlos por objetos administrados, esto no es solución de ninguna manera. La idea de tener hospedado el CLR dentro de SQL Server, es para escribir cierta clase de objetos que con transact SQL pueden llegar a ser demasiado complejos y no tener el mismo desempeño que escritos con lengaujes administrados. Por ejemplo, funciones matemáticas, operaciones de encripción y desencripción, todo lo referente al trabajo con cadenas (lo cual tiene un desempeño pobre con Transact SQL), operaciones como envio de coreo electrónico. Pero entonces nunca podremos suplir el gran desempeño que Transact ofrece para lo que tiene que ver con operaciones de tipo conjunto, es decir consultas sobre la información almacenada dentro de nuestra base de datos, en este contexto, los objetos escritos con lenguajes administrados, van a arrojar un desempeño mucho menor que los de transact. y finalmente cabe resaltar la depuración sobre obejetos escritos en Transact SQL desde el entrono de Visual Studio .NET, con lo cual tengo un elemnto más a favor para poder verificar que está ocurriendo con la jecución de mis elementos no administrados.
Friday, January 19, 2007
Programando Crystal Reports I, una mirada práctica
Quiero en esta oportunidad compartir con ustedes ciertos tips de código y diseño, que me han gastado varias horas de investigación y desarrollo, para poder plasmar el problema de negocio del cliente en una serie de reportes que resultan ser al final muy satisfactorios.
Subreportes:
Este es quizás uno de los temas, por los cuales la gente tiene muchos problemas cuando está construyendo reportes de nivel profesional, los cuales requieren diferentes elementos y jerarquías de cierta complejidad, las cuales pueden ser resueltas con esta característica provista por Cristal de una manera sencilla de utilizar. En esta parte entonces veamos cómo puedo desde mi aplicación ASP.NET y utilizando lenguaje C#, acceder a estos elementos, y adicionalmente poder enviar parámetros, tanto a su reporte contenedor, como a estos subreportes.
Las dos primeras instancias van a ser de tipo SubreportObject y ReportDocument. La primera nos permite definir el representante en código de nuestro subreporte que ya hemos adicionado en diseño al reporte contenedor. La segunda nos permite trabajar con nuestro subreporte, tal como si estuviéramos accediendo un reporte común y corriente, pudiendo acceder a propiedades para fijar por ejemplo la fuente de datos y parámetros si son necesarios.
SubreportObject subreportObject;
ReportDocument subReporte;
Reporte miReporte = new Reporte();
DataSet miDs = new DataSet ()
miDs = Consultar();
subReporte = new ReportDocument();
subreportObject = miReporte.ReportDefinition.ReportObjects["subReporte"] as SubreportObject;
subReporte = miReporte.OpenSubreport(subreportObject.SubreportName);
subReporte.SetDataSource(miDs.Tables[0]);
Adicionalmente a las dos instancias mencionadas, se tiene entonces un objeto de tipo DataSet, el cual servirá como contenedor para la data devuelta por el método que consulte la información desde algún repositorio, por ejemplo una base de datos. Es importante observar la colección ReportObjects, en la cual le indicamos al objeto subreportObject que será el contenedor de un subreporte llamado “subReporte”, nombre asignado en la propiedad Name del subreporte. Si no se tiene cuidado con el nombre asociado al subreporte, al ejecutar la aplicación, el Sistema lanza una excepción que indica que el índice está fuera del rango. Se utiliza un el operador “as”, el cual ofrece seguridad y eficiencia en este proceso, y adicionalmente no tenemos que hacer ni tratamiento de NULL ni manipular excepciones, pero esto será tema de otra entrada.
Entonces es claro que el objeto subreportObject, se ha vuelto el representante en código del subreporte, y por ejmplo lo pasamos como parámetro al método OpenSubreport() con el cual abrimos el subreporte y lo asignamos al objeto subReporte, con el cual finalmente asignamos la fuente de datos al subreporte. Es claro que cuando tenemos un reporte principal, con diferentes subreportes, podemos tener diferentes fuentes de datos para alimentar cada elemento del reporte principal, incluyendo la fuente de este último.
Finalmente podemos entonces consultar la información para el reporte principal.
miDs = new DataSet();
miDs = ConsultarPrincipal();
miReporte.SetDataSource(miDs.Tables[0]);
this.crvVisorReporte.ReportSource = miReporte;
this.crvVisorReporte.DataBind();
El objeto crvVisorReporte es una instanacia de la clase CrystalReportVoewer, que es el visor o control que encontramos en el ToolBox de Visual Studio .NET, el cual nos permite visualizar nuestros reportes en las páginas .aspx o Windows forms.
Quiero anotar algo, y es que en el método SetDataSource(), le pasamos el DataSet y se le indica el objeto Table de la colección que se va a utilizar. Si no se hace esto y si le pasamos solamente el objeto DataSet, inmediatamente el objeto crvVisorReporte, despliega uso campos para ejecutar logon contra la base de datos.
Pasar parámetros al Reporte y Subreportes
Primero que todo es importante conocer las posibles razones por las que ocurre el error más típico cuando se utilizan parámetros en Crystal reports que se envian desde el código de nuestra aplicación. Básicamente el error es Missing Parameter Value y Referencia a objeto no establecida. Esto es lo único que Crystal nos muestra en el visor. Ocurre cuando se han declarado parámetros en el reporte y subreportes, y no se les está enviando ningún valor. También puede ser que desde código estamos asignando un valor a un parámetro que no se ha definido aún en los reportes. Entonces se debe tener un poco de cuidado y así evitar este error que es difícil de depurar algunas veces.
miReporte.SetParameterValue("anio", 2004);
miReporte.SetParameterValue("mes", 1);
El código anterior está asignando los valores a 2 parámetros que deberán estar definidos en nuestro reporte principal y sus subreportes, bueno si es que los subreportes los necesitan, es decir si se requiere utilizar los parámetros en el subreporte deben definirse en el mismo. La manera de pasar los valores enviados desde código entre el reporte principal y sus subreportes es fijando en la opción Change subreports links, que se puede acceder en tiempo de diseño, parándose sobre el subreporte y haciendo clic derecho. Se abre entonces una ventana donde podemos seleccionar uno o más campos sobre los cuales se enlazan los parámetros que hayamos fijado en el reporte principal y sus subreportes. Esto es porque no podemos asignar valores a parámetros de subreportes directamente desde código.
En conclusión, una tarea que puede volverse dispendiosa cuando se hace por primera ves, realmente con el modelo de objetos de Crystal reports desde C#, realmente es sencilla y muy poderosa. Por ejemplo podemos asignar desde código valores a los parámetros, de acuerdo a determinada opción, y ya en el reporte, el valor de ese parámetro determinara el comportamiento del reporte, por ejemplo mostrar u ocultar una de sus secciones.