Tuesday, July 26, 2011

Using Client Reporting to generate PDFs on the fly

Objective: To create PDF file(or any type supported by RDLC) from an ASP.Net(any .net) application from a in-memory dataset.

First, lets create an ASP.Net application using the new Project creation wizard.

Add a new project item and chose Report(report1.rdlc)

image

What we need now is a dataset, which we can use in reports. To do this, lets add a new item – dataset.

image

lets name it as MyDataset.XSD. Lets open this dataset and add a table using the toolbox. Lets name it as DataTable1 and lets add two columns here Email and User. Lets compile this once so that we generate the dataset model.

image

Next step is to use this in our reports. Lets open the report designer(double click the RDLC added earlier) and add a DataSet to this. On report data panel, say New->Data Set.

image

Now select the Mydataset as the data source and the default table as dataset. Now we have Email and User as columns. Lets leave the name as DataSet1. Now its ready to be used in the report.

If everything goes right, you can add a tablix and associate the DataSet1 with it. Now you should see the fields available for using in reports.

image

I have mapped user and Email fields in the report.

Now lets focus back to our code. Open the markup and drop a report viewer control. This should look something like this.

 
<rsweb:ReportViewer ID="repViewer" runat="server">
</rsweb:ReportViewer>




Lets prepare the data to fill the report data using the C# code. We can do this in two ways.




  1. Use the same dataset(MyDataSet)


  2. Create a new dataset on the fly.





Lets try the second approach. On the code behind, lets create a data set and add a table to it and add some data.




            DataSet ds = new DataSet();
DataTable dt = new DataTable();

dt.Columns.Add("User");
dt.Columns.Add("Email");


dt.Rows.Add("Guru", "Guru@aa.com");
dt.Rows.Add("Guru", "Guru@aa.com");
dt.Rows.Add("Guru", "Guru@aa.com");
dt.Rows.Add("Guru", "Guru@aa.com");
ds.Tables.Add(dt);


Now, lets bind this to the report.



repViewer.Reset(); // follow the same order, reset and then add the data   repViewer.LocalReport.ReportPath = "Report1.rdlc";

reportDataSource rs = new ReportDataSource();
rs.Name = "DataSet1"; // Important: Use the same name as given in the report.
rs.Value = ds.Tables[0];

repViewer.LocalReport.DataSources.Add(rs);



Finally, rendering part. Lets return the response as a PDF file



string mimeType;
string encoding;
string extension;
Warning[] warnings;
string[] streamids;

byte[] reportBytes = repViewer.LocalReport.Render("PDF", null,
out mimeType, out encoding,
out extension, out streamids,
out warnings);

Response.ClearContent(); Response.ClearHeaders();
Response.AddHeader("Content-Disposition", "inline; filename=Test.pdf");
/ Response.AddHeader("Accept-Header", reportBytes.Length.ToString());
Response.ContentType = "application/pdf";
Response.OutputStream.Write(reportBytes, 0, reportBytes.Length);
Response.Flush();
Response.Close();


Only advantage of creating the dataset using XSD(MyDataSet) is that you can use them in the report directly by selecting the element. This will help you a lot while binding. You can also connect to SQL and build your data set and use it for report design. Later you can build your own dataset like we have done and use it by providing the same name.Following SQL statement will do the job for you.



Select 'Guru' as User, select 'Test' as Email





Screen shot below is the output, Pdf displayed in browser embedded. This approach will be a great help if you have specify requirement for printing involving the layout, breaks and header/footer.



image

Monday, July 18, 2011

SSRS Record & Play testing

In my previous article I wrote about SSRS performance measurement. This time we will take a look at how we can make use of this data for our performance testing itself.

To test we would need the report, report path and the parameters; all of these we can get from the report server itself

Select catalog.name, [Catalog].Path,
ExecutionLog.Parameters
from ExecutionLog with (nolock)
inner join
Catalog on Catalog.ItemID= ExecutionLog.ReportID


This is like our manual tests being recorded, lets see how we can use this for replaying this rest.



As you know, we can access SSRS reports directly from  the server using reportServer web service URL. Using this URL we can pass query strings too!



<iframe height="100px" width=100%" src="http://172.16.150.29/ReportServer/Pages/ReportViewer.aspx?#ReportPath/ReportName#&amp;rs:Command=Render&amp;#Parameters#"/>


Use excel to import the results and use =CONCATENATE(A2,B2,C2,D2,"""/>")  in excel to generate this HTML code. Now copy these line and place it between HTML tags and your page which can open all reports in one go is ready.



Open this HTML page from multiple machines or tabs; so that it generates sufficient load. Use report server ExecutionLog view to measure the performance of your test.


Monday, July 11, 2011

How bad is your SSRS report performance?

In SSRS, the execution data is stored in a table called ExecutionLogStorage table in ReportServer database. You can view this data using the views provided by SSRS named ExecutionLog3, 2 and ExecutionLog. All these views read the data from same table, with only difference is that they interpret data and give you different views.

Following query will give you data on which report sucks.

use ReportServer
go
-- overall suckers
Select Catalog.Name,
TimeDataRetrieval+TimeProcessing+TimeRendering as TotalTime,
ExecutionLog.Format, ExecutionLog.[Parameters],
ExecutionLog.TimeDataRetrieval,
ExecutionLog.TimeProcessing, TimeRendering,
TimeStart, TimeEnd, Status, ByteCount, ExecutionLog.[RowCount]
from ExecutionLog with (nolock)
inner join Catalog on Catalog.ItemID= ExecutionLog.ReportID
order by TotalTime DESC

-- Query sucks
Select Catalog.Name,
TimeDataRetrieval+TimeProcessing+TimeRendering as TotalTime,
ExecutionLog.Format, ExecutionLog.[Parameters],
ExecutionLog.TimeDataRetrieval,
ExecutionLog.TimeProcessing, TimeRendering,
TimeStart, TimeEnd, Status, ByteCount, ExecutionLog.[RowCount]
from ExecutionLog with (nolock)
inner join Catalog on Catalog.ItemID= ExecutionLog.ReportID
order by TimeDataRetrieval DESC

-- SSRS Processing sucks
Select Catalog.Name,
TimeDataRetrieval+TimeProcessing+TimeRendering as TotalTime,
ExecutionLog.Format, ExecutionLog.[Parameters],
ExecutionLog.TimeDataRetrieval,
ExecutionLog.TimeProcessing, TimeRendering,
TimeStart, TimeEnd, Status, ByteCount, ExecutionLog.[RowCount]
from ExecutionLog with (nolock)
inner join Catalog on Catalog.ItemID= ExecutionLog.ReportID
order by TimeProcessing DESC

-- SSRS Rendering sucks
Select Catalog.Name,
TimeDataRetrieval+TimeProcessing+TimeRendering as TotalTime,
ExecutionLog.Format, ExecutionLog.[Parameters],
ExecutionLog.TimeDataRetrieval,
ExecutionLog.TimeProcessing, TimeRendering,
TimeStart, TimeEnd, Status, ByteCount, ExecutionLog.[RowCount]
from ExecutionLog with (nolock)
inner join Catalog on Catalog.ItemID= ExecutionLog.ReportID
order by TimeProcessing DESC

Sunday, July 10, 2011

Very good UML Video tutorial

A very nice UML Video tutorial to start with.

Tutorial starts with basics and goes all the way. I would recommend you downloading Visual Paradigm for UML along http://www.visual-paradigm.com/product/vpuml/. With this tutor you can learn both UML as well as the tool.  VP can do a lot more, but very good start for both UML and visual paradigm is this.

Special thanks to PatriaLivreBrasil.

*don’t go by the first few videos, it might look slow and boring. Rest of the tutorials are really good.

Sunday, February 13, 2011

Popping up Fancybox from SSRS Report

When do I really need it? You might want user to take some action depending on the report you are showing up. Ex. You are generating a report for 3 products and its performance over a period, now you may want to put the product on the showcase.

What you really do is, call a page and pass the product id to it and user might want to add some comments and take some action.

How do I do it? Using Javascript. You create a text field and set the action on the text field as Go to URL. In the URL field add the expression as

="javascript:void(MyScriptMethod('"+First(Fields! ProdId.Value, "DataSet1")+"'))"


Now the method should be residing on the page which is hosting the report viewer control.



function MyScriptMethod(prodId) {

// fancy box enabled link
var link = $("#lnkProdEval").attr("href");

//remove the old query string parameter
link = link.split('?')[0];

//add a new parameter - product Id
link = link + '?Id=' + prodId ;
$("#lnkProdEva").attr('href', link);

//now click the link through the code.
// Make sure you put this link in a invisible div, so user cannot
// click it directly
$("#lnkProdEval").click();
}


lnkProdEval is the ID of the Anchor, on which fancy box is enabled. You can get more detail about hooking a fancy box @ http://fancybox.net/howto