Monday, December 14, 2009

Dynamic Report Generation Using SSRS Local Reports

Recently, I’ve been working on re-architecting part of an invoicing application that has a very rigid invoice formatting design into an application that will eventually allow for almost infinitely customizable formatting in a very efficient manner.  This should lead to better customer relationships and fewer software customizations and therefore improved ROI.

PROBLEM


Think about your water bill.  Does it have all the information on it that you want?  Let’s assume that it doesn’t.  Suppose your water bill only shows the activity for the current period on it and how much money you need to pay the water company.  This is probably enough information for most people.  But, let’s suppose you’ve recently invested in some new appliances in an effort to conserve water (and money).  Now you’re probably interested in having a little more information on your invoice.  For example, maybe you would like to also see your activity for the previous period and for the same period a year ago.  This information would allow you to easily see how well your investment in new appliances is paying off (your ROI).  Now suppose that you called up your water company and asked them to add this information to your monthly invoice.  I can hear you laughing.  I can hear the water company laughing too.  Well, this is exactly the type of requests that the company I’m working for gets on a weekly basis. 

Their solution up to this point has been to have available in their custom billing software a handful of predefined hard-coded formats from which customers can pick.  However, almost monthly we get a request that doesn’t fit the mold of one of these predefined formats.  When this happens, we have to weigh up the cost of implementing a new format against the amount of money we believe we’ll make from this customer in an effort to determine if its going to pay off to give this particular customer exactly what they want or if we have to tell them that they’ll have to make due with what’s available.  Obviously, only one of these answers leads to exceptional customer satisfaction (and hopefully retention).  Of course, we want to give every customer exactly what they want but this is the real world and code changes still mean considerable investment. 

Oh, and did I mention that all of the current invoices are built using a PDF API?  This makes it very difficult for a programmer new to the app to make modifications if he’s never used the API before.
So, there are several problems here that need to be solved.
  1. Give every customer the same level of exceptional support regardless of contract size.
  2. Allow for more dynamic invoice styling while keeping a uniform appearance and format across all invoices.
  3. Eliminate use of the PDF API to make future application modifications faster (and cheaper) to implement.
  4. Eliminate the need to measure ROI of customer satisfaction.

PROPOSED SOLUTION


The solution to this problem that I proposed is to first eliminate the use of the PDF API and replace it with a SQL Server Reporting Services (SSRS) solution.  For this application I will use SSRS in local reporting mode.  This eliminates the need for a new dedicated reporting server.  We may later add an SSRS server but at this time the added cost and administration was unwanted.  Also, using SSRS in local mode gives us more options for customization of report definitions.

The basic format of all invoices is the same.  There’s a header, a footer, and a body which contains all of the “real” information that customers are interested in (and in customizing).  So, my plan is to build a main SSRS report that would have the header & footer information and a placeholder for the body of the invoice.  The body would be a separate subreport.  This subreport is the part we’ll be interested in for the rest of this article.  The basic idea is replace this subreport with customizations per customer.

I decided to approach this in two different phases.  First, I will get the basic workings of this new approach in place by building a framework that allows the app to create SSRS reports to be exported as PDF files and put the tools in place to allow the subreport to be easily replaced with a customized version per customer.  In this first phase, I decided to create several static subreports (.rdlc files stored as embedded resources) using the Visual Studio reporting designer that would just replace the formats that were currently available via the PDF API code.  This would allow me to focus on the SSRS code without having to worry about any new UI pieces.  Depending on the customer’s current invoice style selection, I will dynamically replace the subreport placeholder with the appropriate subreport.  Completion of this phase should also help me determine if there are any limitations in SSRS local reporting that would prevent this proposal from working as planned.  This is where I am today and what I’ll be detailing in the rest of this article.

After the first phase is completed and working as planned.  The second phase of the project will be to replace the embedded resource subreports with user designed subreport layouts.  This phase should be rather easy since SSRS (.rdlc) files are really just XML files.  The most difficult part of this phase will be getting the UI designer right.  It’ll be more tedious than difficult and I’ll document it at some later date when its complete.

IMPLEMENTATION


This should be very easy, right?  It all sounds very simple.  Create a report definition in the designer and drop in a subreport and then at runtime figure out which subreport should be there and modify some subreport name property and there you go.  Right?  Wrong.  Actually, its not much more difficult than that once you finish this article and benefit from my week of research and experimentation and a call to Microsoft tech support (which eventually led to them telling me it wasn’t possible…).

So, I started by creating my report definitions and getting them all tied into the subreport selection logic and data sources.  This all compiled and executed properly and it seemed that I was done.  Until I noticed that the subreports weren’t actually being replaced and all I was actually getting was the subreport that compiled into the main report definition.  Well, this wasn’t going to work.  I was basically doing something like the following.

public virtual void RenderReport(SsrsReportTypes reportType)
{
    LocalReport report = new LocalReport();
    report.ReportEmbeddedResource = "DynamicSubreporting.Reports.DefaultInvoice.rdlc";
    report.LoadSubreportDefinition("InvoiceBody2", GetSubreportStream());
    report.Render(...);
}

It seemed that no matter what I did I could not get SSRS to recognize the new subreport definition provided it by LoadSubreportDefinition().  Originally, I thought the problem must have to do with the way I was binding the subreport or the way that the subreport was identified by the LocalReport object.  I tried everything I could think of to make this work.  I desperately needed it to work for this project to be successful.

An alternate solution to this problem that I experimented with and got working was to dynamically write out the main report definition and replace the subreport name in the XML prior to writing out the file.  This solution had several limitations that I didn’t want to have to compromise on.  Mainly, it was going to be much messier to utilize this in phase 2 of the project and would probably lead to some multithreading and performance issues.

I read every article on the net that I could find regarding the LocalReport object and it appeared that no one had ever tried doing exactly what I was doing before and documented it or perhaps they just didn’t have problems…  Regardless, this seemed like a good reason to document it and here we are…

So, after reading everthing I could find and trying everything I could think of I finally resorted to creating a Microsoft support ticket and eventually got in contact with a Sr. SQL Server support engineer.  I described the problem and even created a stand-alone project that could be run without the rest of the app & database that he could use to debug my solution (you can download it here).  After a couple days of sending emails back & forth and him talking to his colleagues, they finally came to the conclusion that what I wanted to do just wasn’t possible without using some sort of work-around hack and he sent me a snippet of text from the LocalReport object documentation
The ReportViewer control requires the definitions for all subreports before it can process a report. If the local report was loaded from the file system by specifying the ReportPath property, the ReportViewer control automatically loads the subreports from the file system. In cases where the local report was not loaded from the file system, these methods may be used to load subreport definitions.
This statement turned on a light bulb.  Up until this point, I’d always thought the problem had something to do with LoadSubreportDefinition().  Now I was thinking perhaps the problem was actually in the ReportEmbeddedResource property.  Although this property wasn’t mentioned in this quote of the documentation I thought maybe since I was pulling the embedded resource version of the report that it was also automatically pulling the subreport definitions at that time and I had lost my opportunity to override the subreport.  That’s when I remembered seeing a function called LoadReportDefinition().  Suddenly, it made sense that maybe this function and LoadSubreportDefinition had to be used together.  Sure enough, by replacing ReportEmbeddedResource with a call to LoadReportDefinition() all of my problems were solved.

public virtual void RenderReport(SsrsReportTypes reportType)
{
    LocalReport report = new LocalReport();
    report.LoadReportDefinition(GetReportStream("DynamicSubreporting.Reports.DefaultInvoice.rdlc"));
    report.LoadSubreportDefinition("InvoiceBody2", GetSubreportStream());
    ...
    report.Render(...);
}
In hindsight, this seems like such a simple problem but it ended up taking a lot of time to get working and since there seems to be little information on solutions using the LocalReport object on the web, I thought that this article seemed worthwhile.  Hopefully, someone will gain something from it. 

The complete demo project can be downloaded here.  There’s a lot more to making this work that I didn’t discuss that you can easily decipher from the code.  This project was created by simply stripping out the bare minimums needed to demonstrate this one piece of functionality.  Nothing else about it should be considered an example of model coding practices.

CONCLUSION


The SSRS LocalReporting object can be used to create a truly dynamic reporting framework that requires a minimal amount of coding yet provides a great amount of power and functionality.  If you find that you are regularly making code “enhancements” in order to provide custom-specific reporting, this approach should be examined to see if it can help you make a real enhancement that provides real ROI.

Wednesday, October 28, 2009

U.S. Census Regions

Tonight I was working on a project for my A.I. class I’m taking at MTSU and had a need to have a table of U.S. regions that I could use to join to some other data to allow me to regionalize the data.  I thought that a quick Bing search would have revealed all that I could handle and the problem would be solved.  Well, it turns out that all I could find were all the maps galore but no where could it be found in tabular form.  So, I had to hand enter it based on this info from the U.S. Census Bureau website.  I decided it might be good to post it here for others searching for it and for that time a year from now when I need it again.  It’s totally denormalized for easy use in Excel.  For database work, you’ll probably want to normalize into states, regions, & divisions tables.  Enjoy!

RegionNum Region DivisionNum Division State
1 Northeast 1 New England CT
1 Northeast 1 New England ME
1 Northeast 1 New England MA
1 Northeast 1 New England NH
1 Northeast 1 New England RI
1 Northeast 1 New England VT
1 Northeast 2 Middle Atlantic NJ
1 Northeast 2 Middle Atlantic NY
1 Northeast 2 Middle Atlantic PA
2 Midwest 3 East North Central WI
2 Midwest 3 East North Central MI
2 Midwest 3 East North Central IL
2 Midwest 3 East North Central IN
2 Midwest 3 East North Central OH
2 Midwest 4 West North Central ND
2 Midwest 4 West North Central SD
2 Midwest 4 West North Central NE
2 Midwest 4 West North Central KS
2 Midwest 4 West North Central MN
2 Midwest 4 West North Central IA
2 Midwest 4 West North Central MO
3 South 5 South Atlantic DE
3 South 5 South Atlantic MD
3 South 5 South Atlantic DC
3 South 5 South Atlantic WV
3 South 5 South Atlantic VA
3 South 5 South Atlantic NC
3 South 5 South Atlantic SC
3 South 5 South Atlantic GA
3 South 5 South Atlantic FL
3 South 6 East South Central AL
3 South 6 East South Central KY
3 South 6 East South Central MS
3 South 6 East South Central TN
3 South 7 West South Central AR
3 South 7 West South Central LA
3 South 7 West South Central OK
3 South 7 West South Central TX
4 West 8 Mountain AZ
4 West 8 Mountain CO
4 West 8 Mountain ID
4 West 8 Mountain NM
4 West 8 Mountain MT
4 West 8 Mountain UT
4 West 8 Mountain NV
4 West 8 Mountain WY
4 West 9 Pacific WA
4 West 9 Pacific OR
4 West 9 Pacific CA
4 West 9 Pacific AK
4 West 9 Pacific HI

Tuesday, May 19, 2009

Dare to Dream Different Video Has Been Submitted

Actually, it was submitted last Friday about 5 hours before the deadline…  I’ll make another post later that goes into the details of the project design. 
Damnit Jim!  I’m a programmer not a videographer!

Tuesday, February 17, 2009

Dare to Dream Different Contest Extended

D2DDLogoI just received this email from Marden-Kane, the administrators of the contest.  It now looks like I have an extra month and a half…

Dear Dream Different Contest Semi-Finalist:

Due to unforeseen delays in the delivery of development kits to some Round 2 qualifiers, we are extending the Round 2 entry deadline to help ensure that everyone has enough time to develop their prototype devices.  Round 2 entries are now due no later than 11:59 p.m. PT on May 15, 2009.  It is estimated that Round 3 will start on June 15, 2009 and end on July 31, 2009, but the exact dates will be determined on or around the close of Round 2, based on the availability of selected Finalists to travel to a designated demonstration location. 

For full details, please see the updated Official Rules at http://www.dreamdifferentcontest.com/rules.aspx.
On behalf of the entire Dream Different Contest team at Microsoft and Marden-Kane, we thank you for your patience and understanding.

Thank you very much,

RF

Marden-Kane Inc.

Friday, January 30, 2009

The Goodies Have All Arrived!

I’m a little late in writing this since most of the hardware arrived last Friday, January 23rd, but better now than never…

IMG_8988From Microsoft, I received my development kit for the Dare to Dream Different Challenge.  Included in the kit was the Device Solutions Tahoe-II board with an XBee chip, a USB XBee development board, a one page “Getting Started” pamphlet and a disk with the Tahoe-II SDK on it.  I wasn’t expecting the USB XBee development board and it was a nice surprise since I had already decided on XBee as my communications technology.  I already have an idea on how to utilize this board.

So far, I’ve run several sample programs through the Tahoe-II and am very impressed with how responsive it is and how easy Visual Studio 2008 makes it to develop for this board.  The real challenge it seems at this point is going to be figuring out how to communicate using XBee.  Although the board has support for XBee, I’ve found that the .net Micro Framework 3.0 does not yet have this support.  Fortunately, Michael Schwartz has an open source project on CodePlex that appears to make working with XBee somewhat trivial.  I do wish there was more documentation and sample code available.

IMG_9001 Since I am also going to need a relay board, I decided on the ZUXPSR1610ProXR 16-channel 10 amp Zigbee (XBee) relay board by National Control Devices.  Since I haven’t figured out exactly what my relay requirements are going to be yet, I went ahead with this one.  It’ll probably have more relays than I need and will certainly handle more current, but I thought that might come in handy after the competition when I decide to use it for other projects.  There is no documentation that came with this board.  However, I presume that switching the relays will basically be the same was communication with the GPIO ports on the Tahoe-II.  I ordered the board with an external antenna since my plan is to eventually house this board in a project box. 

The board operates on a 12V DC power supply which happens to be a big bonus for me since I designed the circuitry for the LEDs on the mock yard to be powered by 12V DC.  The board has a set of terminals where I can run the 12V power off of it.  This is great since it means that I will not have to use two power supplies (one for the board and one for the mock yard).  Something isn’t quite the same as that of the power supply I had been using to build the mock yard with and I’ve discovered that my green LEDs do not shine very brightly when powered from the board.  I’ll have to go back and replace the resistors on the green zone circuit to some with lower resistance.  I’ll do this later on after I have the software working.  As long as the circuit is working well enough for me to tell that the relays are working, I’ll be fine with it for now.

IMG_9008 Speaking of the mock yard…  I put the finishing touches on it earlier this week.  I used some model train supplies I picked up at Hobby Lobby to decorate the foam board with “grass” and a gravel driveway.  Unfortunately, the application of watered down glue used to adhere the “grass” also has caused my foam board to bow up.  This is very frustrating.  I’m thinking that if I spray some water to the bottom side of the board that it may equalize out the shrinkage and cause it to straighten out.  I will also be replacing the bottom piece of foam board with a piece of 1/4” acrylic Plexiglas.  This will allow the circuitry to be easily viewed.  I’ll probably also screw down the sides of the foam board to the Plexiglas to force it to lay flat.  Again, these are all projects that will have to wait until later when everything is functional.  Like the old bird house I found in the garage? :) 

Also, notice that I ran all of the lead wires for the LED circuits to an RJ45 plug mounted on the side of the foam board.  This will make it much easier to separate the parts for transportation when I have to go to Redmond to demonstrate the project for Round 3 ;)  Since I only need three pairs of wires for my three zones and the RJ45 port allows for four pairs, I went ahead and ran a pair of wires under the house that I can use to do some yet undetermined very cool thing with on the house.  Any ideas?

Well, now the real fun has begun.  The task of learning the .net micro Framework and XBee.  Since I don’t really plan to disclose all the details of my development from here out until after the competition is over, I’m not really sure how often updates will continue to be posted here on the blog.  I’m sure there will be some screen shots.  So, stay tuned.

Tuesday, January 20, 2009

Yellow Zone is alive!

IMG_8939Well, I think you get what’s going on here by now.  So, this is the last of the LEDs that have to be wired for the mock yard.  One thing you may notice different about this zone is the little zone within it.  This is going to be a garden area.  In case you don’t know about invisible dog fences, it is possible to create zones like this to keep dogs out of your garden, porch, etc.  This little zone will always be activated when the yellow zone is activated.  The Tahoe-II still hasn’t arrived...  Tomorrow, I’ll start decorating the mock yard and try to make it look like a little more than a piece of foam board with lights.  Must now get some sleep…

Friday, January 16, 2009

The Green Zone is Alive

The electronic parts I ordered for the IMG_8846model yard arrived early, so it was time to start soldering again.  Tonight I got all of the green LEDs in place and wired up.  This is a much more tedious task than you would think from looking at the end results.  I also finished up the last section of the red zone.

I’m taking a little time off and heading to Savage Gulf for three days of backpacking with Jr. this weekend.  So, I guess it’ll be Monday night before I can finish up the yellow zone.  If we don’t freeze to death (my thermometer says its 8.8° F right now…) then there should be a trip report here after I get back.

With any luck maybe the Tahoe-II will arrive early next week.