Reportes Web
Reportes Web
Es importante mencionar que los datos almacenados son útiles en la misma medida que
se puedan convertir en información para las personas que los necesitan. También es
importante subrayar que la plataforma tecnológica que utilicemos debe poder tener
facilidades para convertir los datos en información y poder entregarlos a los usuarios de
forma que sean útiles.
Primero debemos entender que estamos haciendo y porqué. El caso es el siguiente, por lo
regular, un reporte será impreso en hojas de papel con un tamaño específico, esto nos
lleva al primer desafío, tenemos que poder entregar información que quepa dentro de
estas hojas de papel y que pueda ser impreso sin problemas a una impresora local o
remota. El segundo desafío es poder realizar esta operación desde una aplicación web;
pero ¿Cuál es el problema aquí? Pues muy sencillo, lo que sucede es que una página web
es un objeto que tiene un despliegue único, es decir, todos los datos se muestran en una
“tira” continua de información lo que hace difícil sino imposible hacer que la impresión sea
consistente y predecible. El tercer desafío es poder hacer que la solución sea utilizable
para “cualquier navegador” lo que en si es todo un reto.
En primer lugar, debemos decir que un reporte en una aplicación web se compone de 4
partes principales: 1) Un documento reporte. 2) Un objeto ReportViewer. 3) Una fuente de
datos. 4) Una página que permita mostrar al ReportViewer.
Entonces, manos a la obra. Hagamos un reporte que: pueda controlar los saltos de página
por medio de una configuración de tamaño de papel, tenga una forma de imprimir
correctamente la información y sea “crossbrowser” (pueda ser visto en cualquier
navegador).
Inicialmente debemos crear una aplicación web (o website si lo prefieren) y para ello
vamos a Visual Studio 2008 y seleccionamos el tipo de proyecto que nos interesa:
Esto creará una estructura similar a la siguiente:
Ahora ya tenemos dos de las cuatro cosas necesarias para el reporte, vamos por la
tercera. Es necesario crear un documento de reporte que contenga la definición de los
datos que quiero mostrar.
El ejemplo que voy a demostrar utilizará la base de datos Northwind y los datos
almacenados en las tablas dbo.Orders y dbo.Order Details. En esta primera parte
solamente veremos la tabla dbo.Orders y dejaremos la otra tabla para las siguientes partes
de este artículo.
Ahora es necesario decirle al reporte que datos quiero que muestre, para ello agregamos
un Data Source hacia SQL Server por medio de Server Explorer y seleccionamos la base
de datos Northwind, lo que debería mostrar la siguiente información:
Luego al crear el nuevo Data Source tenemos que se creó en realidad un objeto DataSet,
el cual tendrá la definición de nuestros objetos de datos. Entonces solamente arrastramos
del Server Explorer las tablas que nos interesan hacia el objeto DataSet. En este caso es
dbo.Orders. Una vez grabado el DataSet, en la ventana de Website Data Sources se
mostrará la tabla y los campos disponibles.
Ahora agregamos un objeto List a nuestro reporte para que contenga los datos de la tabla:
Y por último arrastramos los campos que queremos de nuestra tabla hacia el objeto list.
Para nuestro ejemplo y con fines ilustrativos solamente arrastraremos 4 campos: OrderID,
OrderDate, ShipName, ShipCity. También agregaremos unas etiquetas para saber de qué
datos estamos hablando. Cabe mencionar que el documento report no tiene Labels como
los conocemos para esta tarea así que tendremos que utilizar TextBoxes para poder poner
títulos a los campos:
Bien, grabamos nuestro reporte y nos pasamos a la página Default.aspx que es donde
tenemos nuestro objeto ReportViewer y ahora le decimos que utilice el reporte que recién
acabamos de crear:
Aquí podemos ver claramente en la página que el reporte funciona correctamente, tiene 76
páginas y se muestra con un zoom de 100%.
Espero que este artículo les sirva, seguramente se encontrarán con alguna necesidad para
utilizar estos objetos y ojalá les ayude a lograr su cometido.
La primera línea es la directiva que indica a IIS que es una página ASP.NET escrita en lenguaje C# y que
su archivo de Code Behind es Default.aspx.cs. Para quienes no estén muy empapados con el tema; el
archivo Code Behind es un archivo asociado a la página donde se realiza toda la programación de
métodos y eventos que nos servirán para la manipulación de objetos en nuestra página. El archivo, en
este caso, tiene la extensión .cs que indica que es en lenguaje C#; y luce algo así:
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
namespace Report.Part1
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
<spa
n style="mso-spacerun: yes"> </spa
}
}
Si están usando Visual Studio 2005, seguramente tendrán en la versión del objeto el número 8.0.0.0. No
hay ningún problema, estos artículos funcionan perfectamente con esta versión de ReportViewer.
Luego tenemos la definición de una página web normal, pero dentro de su definición existen dos
elementos que son los que nos interesan para nuestro análisis. El primer elemento es la definición del
ReportViewer:
<rsweb:ReportViewer ID="ReportViewer1" runat="server" Font-Names="Verdana" Font-
Size="8pt"Height="400px" Width="649px">
<LocalReport ReportPath="Ordenes.rdlc">
<DataSources>
<rsweb:ReportDataSource DataSourceId="ObjectDataSource1" Name="DataSet1_Orders" />
</DataSources>
</LocalReport>
</rsweb:ReportViewer>
</asp:ObjectDataSource>
En este caso, el ObjectDataSource indica que se debe utilizar un tipo TableAdapter que existe dentro de
DatasSet1 y que este adaptador se llama OrdersTableAdapter. Este objeto adaptador es el que conoce
todo lo necesario para poder obtener los datos de la base de datos y devolverlos en el formato que se
necesita. Por último se utilizará un método llamado GetData que tiene la lógica para hacer las llamadas al
adaptador y obtener efectivamente los datos a mostrar.
Bueno, es tiempo de “ensuciarnos” las manos con un poco de programación. Debo decir que en al
principio del artículo encerré entre comillas la palabra “hacer” refiriéndome a hacer reportes por código,
porque esto en realidad no se puede hacer. Los documentos de reporte son documentos que hay que
definirlos de manera gráfica, pero lo que si podemos hacer es manipular todo lo demás por código, y ese
es el primer ejemplo que vamos a hacer.
En primer lugar vamos a quitarle a la página el objeto ObjectDataSource y vamos a quitarle al reporte la
definición del reporte al objeto ReportViewer. Esto nos dejará la página de la siguiente forma:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs"Inherits="Report.Par
t1._Default" %>
Ahora vamos a trabajar con el archivo Code Behind (Default.aspx.cs) y vamos a agregar todo lo que
teníamos antes pero por medio de código en C#:
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
namespace Report.Part1
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ObjectDataSource ObjectDataSource1 =
new ObjectDataSource("Report.Part1.DataSet1TableAdapters.OrdersTableAdapter","GetData")
;
Microsoft.Reporting.WebForms.ReportDataSource rds =
new Microsoft.Reporting.WebForms.ReportDataSource("DataSet1_Orders",
ObjectDataSource1);
ReportViewer1.LocalReport.ReportPath = "Ordenes.rdlc";
ReportViewer1.LocalReport.DataSources.Add(rds);
}
}
}
Como pueden apreciar hicimos exactamente lo mismo, creamos un ObjectDataSource con la definición h
acia el DataSet que teníamos, luego hicimos referencia al documento reporte Ordenes.rdlc y por último
agregamos un ReportDataSource.
Esto produce el mismo resultado de la vez anterior:
Bien, ahora es tiempo de seguir con otro tema importante. El paso de parámetros. Supongamos que es
necesario filtrar por un rango de fechas nuestro reporte, de esta forma se mostrará solamente las órdenes
que correspondan a ese rango de fechas. Existen varias formas de hacer este trabajo pero nosotros
utilizaremos la forma filtrando directamente en la tabla de la fuente de datos, entonces para ello tenemos
que modificar nuestro DataSet de la siguiente forma:
Abrimos el documento DataSet donde tenemos lo siguiente:
Hacemos un click derecho sobre Orders y seleccionamos la opción “Configurar” (Configure en inglés). Y
esto mostrará una ventana con el Query que se hace a la tabla:
Ahora modificamos el query agregando al final: WHERE OrderDate BETWEEN @FechaInicial AND
@FechaFinaly hacienda click al botón Next llegamos al siguiente paso donde el asistente pregunta si se
modifican los métodos para obtener los datos.
Es necesario tener seleccionados todos los checkboxes para que el asistente realice las actualizaciones
necesarias y que incluya los nuevos parámetros de fecha que hemos agregado. Luego hacemos click en
el botón Next y llegamos al final del asistente donde todo debe haber quedado reconstruido y listo para
usarse:
Hacemos click en el botón Finish y luego guardamos el documento que ahora queda modificado en el
método GetData como sigue:
Ahora vamos a modificar nuestra página para agregar dos campos donde se puedan ingresar fechas y
poder manipular nuestro reporte:
El botón será ahora el encargado de hacer que el reporte se ejecute y obtenga los datos, pasando como
parámetros los valores seleccionados de los dos calendarios que indican la fecha inicial y la fecha final. El
código del botón queda de la siguiente forma:
Microsoft.Reporting.WebForms.ReportDataSource rds =
no-proof:="mso-no-proof:" new?;="new?;"
courier="courier"> newMicrosoft.Reporting.WebForms.ReportDataSource("DataSet1_Orders",
ObjectDataSource1);
ReportViewer1.LocalReport.DataSources.Clear();
ReportViewer1.LocalReport.DataSources.Add(rds);
ReportViewer1.LocalReport.ReportPath = "Ordenes.rdlc";
ReportViewer1.LocalReport.Refresh();
}
Podemos estar seguros que el reporte si filtró los datos tan solo viendo que el conteo de hojas dice que el
reporte tiene solamente 3 hojas y no 76 como originalmente tenía.
Bueno, espero que esta segunda entrega haya sido de su agrado y les ayude a complementar lo que ya
habíamos visto. Por favor, esperen el siguiente artículo que tratará sobre subreportes y como manejarlos.
Hasta la próxima.
Construyendo Reportes Para Aplicaciones Web: Parte 3
Bien, continuando con esta última parte de la serie de de reportes para aplicaciones web, vamos a
basarnos en el reporte que ya teníamos antes para hacer una construcción de subreportes o reportes
anidados.
En primer lugar, debemos decir que un subreporte no es más que un reporte independiente que es
definido y llamado desde dentro de otro reporte. En el diseñador de reportes debemos seleccionar el
objeto Subreport y arrastrarlo hasta nuestro reporte.
Hay dos requerimientos importantes que debemos mencionar acerca de los subreportes:
1. La estructura del subreporte debe ser por medio de una tabla (diseño en forma de tabla, no se refiere a
que los datos deben provenir de una tabla de base de datos).
2. En el reporte padre solamente se muestra la parte de “Body” del subreporte (el header y footer son
ignorados).
Entonces, manos a la obra. Empecemos por definir nuestro nuevo reporte que va a ser el subreporte que
llamaremos del reporte que ya teníamos (¿parece trabalenguas verdad?). Para empezar, crearemos un
nuevo reporte al que llamaremos DetalleOrdenes.rdlc y le agregaremos un objeto Tabla de la caja de
herramientas (toolbox).
Ahora, como hicimos antes, vamos a seleccionar la fuente de datos y utilizaremos la tabla dbo.[Order
Details] de la base de datos Northwind. Esto creará un nuevo dataset llamado DataSet2 a donde
podremos arrastrar la tabla de la conexión de la base de datos.
Arrastramos ahora los campos ProductID, Quantity y UnitPrice a cada uno de los campos en la tabla de la
siguiente forma:
Para efectos del artículo no entraremos en detalles del formato y lo dejaremos tal y como está. Ahora es
necesario definir un parámetro para el reporte, el parámetro nos servirá para mostrar solamente aquellos
registros del detalle de la orden que correspondan con la orden misma, una especie de reporte maestro-
detalle. La definición la hacemos en el menú Report y seleccionando la opción Report Parameters. El
parámetro lo llamaremos NoOrden y será de tipo Integer (no olvide chequear el checkbox de la opción
Allow Null Value).
Ahora seleccionamos la tabla y vamos a las propiedades de la misma seleccionando el Tab de Filters.
Esto nos servirá para hacer el filtro del parámetro y que se seleccionen solo los datos que corresponden a
la orden.
Para la definición del filtro, es necesario indicar que el valor para el campo OrderID debe ser igual al del
parámetro NoOrden.
Bien, eso es todo en el subreporte. Ahora regresamos a nuestro reporte principal y hacemos al objeto List
un poco más grande para poder ubicar al objeto Subreport dentro de él:
Ahora debemos configurar nuestro objeto Subreport. Indicaremos primero que utilice el reporte
DetalleOrdenes.rdlc que acabamos de crear, para ello entramos a las propiedades del subreport y en el
Tab General seleccionamos:
Ahora vamos al Tab Parameters donde le daremos el valor a nuestro parámetro y le decimos que el
parámetro NoOrden tomará su valor del campo OrderID:
Ahora, le diremos al objeto List que solamente nos muestre una orden por página, así podremos tener
una mejor visión del efecto del subreporte y veremos claramente la información de la orden. Para lograr
esto iremos a las propiedades del objeto List y en el Tab General haremos click en el botón “Edit details
group”; en la sección Expression seleccionaremos al campo OrderID y luego seleccionaremos la opción
“Page break at end”.
Ahora podemos ejecutar el reporte para ver el resultado de todo lo que hemos hecho:
La ejecución produce dos resultados, uno esperado, uno inesperado. El resultado esperado es que
efectivamente se muestra solamente una orden en cada página. El resultado inesperado es un error al
mostrar el subreporte.
Este error sucede debido a que el modo en que funciona el subreporte. Es necesario indicarle la fuente de
datos, como hasta este momento solamente hemos definido la fuente de datos para el reporte padre el
subreporte no tiene fuente de datos, entonces es necesario definirla, pero no se define en cualquier lado.
Para ello es necesario escribir un poco de código.
Primero vamos al archivo de CodeBehind de la página default, Default.aspx.cs, y definiremos un método
para realizar el procesamiento del evento SubreportProcessing del objeto LocalReport que ya hemos
visto antes. Agregamos al inicio del archivo la siguiente directiva:
Reporting Services –
Generando y Publicando
Reportes Dinámicamente
Personas que lo han encontrado útil: 3 de 4 - Valorar este tema
Principio de la página
Construyendo el RDL
El RDL define la metadata del reporte, lo que al final es una extensión XML que se guía por una
estructura definida para cada área del reporte, tales como el encabezado, los campos que se
muestran en el reporte, los parámetros que éste acepta, etc.
El RDL es un XML que tiene tres elementos básicos:
1. 1. DataSources: Almacena información sobre la conexión al servidor de base de datos.
Ver Figura 2:
3. 3. Body: Define toda la presentación del reporte (colores, tipos de letra, etc.). Ver Figura
4:
Existen más secciones que el RDL puede mantener, tales como los parámetros, agrupamientos,
etc. A través del XMLTextWriter podemos generar un archivo XML que respete la estructura
completa de un archivo RDL. En este caso, de acuerdo a los campos seleccionados, debemos
generar el archivo RDL; para ello debemos conocer qué campos fueron marcados en el Control
List:
private string[] obtenerCampos()
{
int iItems = 0;
foreach (System.Web.UI.WebControls.ListItem item in
lstCampos.Items)
if (item.Selected)
iItems++;
string [] campos = new string[iItems];
int iPos = 0;
for (int i=0;i<lstCampos.Items.Count;i++)
if (lstCampos.Items[i].Selected)
{
campos[iPos] = lstCampos.Items[i].Text;
iPos++;
}
Principio de la página
writer.Formatting = Formatting.Indented;
// Elementos del Reporte
writer.WriteProcessingInstruction("xml",
"version=\"1.0\" encoding=\"utf-8\"");
writer.WriteStartElement("Report");
writer.WriteAttributeString("xmlns", null,
"http://schemas.microsoft.com/sqlserver/reporting/2003/10/reportdefini
tion");
writer.WriteElementString("Width", "6in");
// Elementos del DataSource
writer.WriteStartElement("DataSources");
writer.WriteStartElement("DataSource");
writer.WriteAttributeString("Name", null,
"DataSource1");
writer.WriteStartElement("ConnectionProperties");
writer.WriteElementString("DataProvider", "SQL");
writer.WriteElementString("ConnectString", sConexion);
writer.WriteElementString("IntegratedSecurity",
"true");
writer.WriteEndElement(); // Cadena de Conexion
writer.WriteEndElement(); // DataSource
writer.WriteEndElement(); // DataSources
// Escribiendo el Archivo
writer.Flush();
stream.Close();
}
Principio de la página
Publicando el reporte
Uno de los métodos que expone Reporting Services nos permite hacer Upload del archivo RDL
además de establecer si el reporte va a ser sobrescrito, el nombre del reporte, etc.
private void uploadRDL()
{
ReportingService report = new ReportingService();
report.Credentials =
System.Net.CredentialCache.DefaultCredentials;
Byte[] definition = null;
Warning[] warnings = null;
string name = "NUEVOREPORTE";
try
{
FileStream stream = File.OpenRead(Server.MapPath("/UIReport")+
"/DSIReport.rdl");
definition = new Byte[stream.Length];
stream.Read(definition, 0, (int) stream.Length);
stream.Close();
}
catch(IOException e)
{
Console.WriteLine(e.Message);
}
try
{
warnings = report.CreateReport(name, "/DemoReport", true,
definition, null);
if (warnings != null)
{
foreach (Warning warning in warnings)
{
Console.WriteLine(warning.Message);
}
}
else
Console.WriteLine("Report: {0} created successfully with no
warnings", name);
}
catch (SoapException e)
{
Response.Write(e.Detail.InnerXml.ToString());
Response.End();
Console.WriteLine(e.Detail.InnerXml.ToString());
}
}
Principio de la página
Conclusión
La particularidad de Reporting Services nos da la facilidad de crear interfaces en las que el
usuario pueda elegir tanto los datos como el formato de los reportes. El RDL nos brinda la
alternativa de realizar esto mediante la programación y el servicio Web de Reporting Services nos
expone varios métodos que podemos aprovechar no solo para generar nuevos reportes sino a
la vez para crear nuevos subscriptores para el reporte generado, entre otras cosas.
Carlos Anampa es Técnico en Computación e Informática y estudiante de la Ingeniería de
Sistemas de la Universidad Privada del Norte, Trujillo. Trabaja como Desarrollador desde 1999 y
como Director de Proyectos, estando dedicado al desarrollo de aplicaciones ASP .net como
programador y consultor en diversas compañias privadas en Perú. Es Docente de Especialización
en la UPN y ex- Docente en el Insituto Tecnológico del Norte. Cuenta con la certificación MCSD
sobre .net gracias al programa de becas de Microsoft Perú.