Sunday, April 18, 2010

ActiveReports: Show the Data from the Windows Azure Table Storage

Preface
Some time ago I blogged about using Data Dynamics Reports to visualize the data that are stored in Windows Azure Table Storage. Originally I planned to compose that blog post around both of Data Dynamics Reports and ActiveReports but then I found that the latter has the issues with running against a cloud environment. But ActiveReports team did the great job for preparing Service Pack 1 which includes the fix for those issues! And this blog post repeats the "show the table storage data" walkthrough for ActiveReports6 SP1. I believe that it will be useful for someone.

Windows Azure Table Storage
Windows Azure Table Storage differs from the tables in a relational database. Table storage is object-oriented and saves the entities and the entity properties. The details of table storage is not the subject of this post. I believe that the best way to show how ActiveReports can be used with the table storage is going through the live example. This walkthrough is based on the post in "Cloudy in Seattle" blog. However, that blog post sample uses Windows Azure SDK 1.0 and I used the newer version 1.1. A few things was changed in version 1.1, I highlighted them later in this post.

Walkthrough
This walkthrough is totally based on "Windows Azure Walkthrough: Table Storage". I just aligned it with the changes in Windows Azure SDK v1.1 and added the steps for show the data using ActiveReports.
(1) Get started with Windows Azure - install the prerequisites and SDK v1.1. Install the latest release of ActiveReports6.
(2) Download and extract Windows Azure Additional Samples from MSDN code gallery.
(3) In Visual Studio 2008 or 2010 create the new Windows Azure Cloud Service project.
(4) In the pop-up dialog add C# ASP.NET Web Role into the Cloud Service Solution.
(5) Compile StorageClient solution that is included in the advanced samples obtained on step (2). From the Web Role add the reference to StorageClient assembly.
(alternatively you can include the *.cs files from StorageClient solution to the Web Role project. F.e. I've added them as a link in my test project).
(6) Add the reference to System.Data.Services.Client in Web Role project.
Steps (7)-(23) are the same as in Table Storage Walkthrough. However step 22(Create Test Storage Tables) is not needed with Windows Azure SDK 1.1.
(24) Add several contacts using the form on the default page of the cloud service. We will show the contacts list using ActiveReports.
(25) Add the new ActiveReports 6(code-based) File in the Web Role Project(in the root folder). Name it "Contacts.cs".
(26) Add two textboxes to the detail section of the report. Set DataField property to "Name"(without quotes) for the 1st textbox. Set DataField property to "Address"(without quotes) for the 2nd textbox.
(27) Add the new Web Form in the Web Role Project. Name the new form "ARReportForm.aspx".
(28) Add ActiveReports6 web-based report viewer in ARReportForm page.
(29) Add the following code to the Page_Load event handler of ARReportForm:

var rpt = new Contacts();
var ds = new ContactDataSource();
rpt.DataSource = (from c in ds.Select() select new {c.Name, c.Address}).ToArray();
rpt.Run(false);
WebViewer1.ClearCachedReport();
WebViewer1.Report = rpt;

ActiveReports allows you to use the collection of the objects as the data source, so we can use the result that is returned by ContactDataSource.Select method.
(30) Set ARReportForm.aspx as the Start Page, run the cloud service and you will see that web-viewer shows the contacts that you added on step (24).
End of walkthrough.

Sunday, April 11, 2010

Tips and tricks: Exporting to CSV using Data Dynamics Reports

General
This is the first in a series of posts that will highlight small and simple but useful undocumented features of Data Dynamics Reports and ActiveReports. I have several topics in mind and hope that more ideas about such features come soon. I will start with the answer to question we were recently asked - is that possible to export the report to CSV format using Data Dynamics Reports?

Tip: use Xml Rendering Extension to get CSV output.
That sounds weird, but it's really not. Let me explain. Data Dynamics Reports allows you to save the data that are shown by a report to XML format. The output XML content does not include the information about report items style, pagination, interactivity, etc., it only includes the data that are grouped, sorted, filtered and displayed in the report's data regions. For example say that a report consists of a simple table that has two columns, the header and the details:

The report output in the viewer would look like below:

But the output that is produced by Xml Rendering Extension looks like:

<Report Name="Ages.rdlx">
<Table1>
<TextBox2>Age</TextBox2>
<TextBox1>Name</TextBox1>
<Detail_Collection>
<Detail>
<TextBox5>30</TextBox5>
<TextBox4>Jon</TextBox4>
</Detail>
<Detail>
<TextBox5>27</TextBox5>
<TextBox4>Michael</TextBox4>
</Detail>
<Detail>
<TextBox5>32</TextBox5>
<TextBox4>Bryan</TextBox4>
</Detail>
<Detail>
<TextBox5>29</TextBox5>
<TextBox4>Stewart</TextBox4>
</Detail>
</Detail_Collection>
</Table1>
</Report>

The element names in the output XML file can be fine-tuned using the properties on the individual report items, more details can be found on the corresponding help topic. That's all good, but how to get the same data in CSV format? For example:
Name,Age
Jon,30
Michael,27
Bryan,32
Stewart,29
Xml Rendering Extension has XsltStyleSheet setting, you can specify the stylesheet that will be applied to the output XML before producing the resulting document. Therefore you can write the XSLT that transforms XML content to CSV content. It's of course not possible to implement the uniform XSLT for all the reports, but the example for the table-based report where the table has the 1-row header and 1-row details is shown below. It works for any number of columns:


<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<!--Select the table header content -->
<xsl:for-each select="Report/Table1/*">
<xsl:if test="name(current())!='Detail_Collection'">
<xsl:if test="not(position() = 1)">,</xsl:if><xsl:value-of select="current()" />
</xsl:if>
</xsl:for-each>
<!--Insert the line break-->
<xsl:text>
</xsl:text>

<!--select the table details content -->
<xsl:for-each select="Report/Table1/Detail_Collection/Detail">
<xsl:for-each select="current()/*">
<xsl:if test="not(position() = 1)">,</xsl:if><xsl:value-of select="current()" />
</xsl:for-each>
<!--Insert the line break-->
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>