views:

34

answers:

1

My company has a Windows Forms application that leverages ClickOnce and .NET Remoting (soon to be WCF) to a back end IIS web application that handles all data access operations. For reporting, we currently use SSRS and have the distributed clients connect directly to the report server.

We would like to potentially remove, or augment, our dependency on SSRS reports with the ability to support local reporting via RDLC files. One hurdle that I have to overcome is that it is unlikely that the individual client machines will have direct access to the database and therefore would require the report data to be fetched from the web application over our Remoting or WCF transport layer.

"Discovering" the parameters of the report for purposes of dynamically building a UI of report parameter prompts I don't think is terribly difficult, but actually telling the back-end system what class/method to invoke in order to return the correct data for the report is less simple.

Has anyone experimented with somehow embedding information into the RDLC file (either through a comment in the report or otherwise) that can be used as a "hint" to the server application layer for determining what method to execute? It's likely that the actual RDLC will be stored in the database versus being distributed with our application.

Any insight or guidance would be appreciated.

-MrB

A: 

Check out www.gotreportviewer.com and the RDL viewer example (last sample on right side). It already has the code to load up the RDLC and parse the XML file to get the parameters out as well as the connection information and query information. Armed with that you should be able to use the backend to get everything and populate/load the report and run it.

As far as adding a hint or comment in the RDLC somewhere to specify what call to make I would suggest perhaps instead just using the report name as the hint. We've done this in the past for our reports to know what to call to load the data.

We've done something like this before :

VB Version:

Select Case GetReportName()

            Case "SiteEval"
                Using adp As New DataSetsTableAdapters.SiteEvalTableAdapter, _
                 objDT As New DataSets.SiteEvalDataTable
                    adp.Fill(objDT)
                    objLR.DataSources.Add(New ReportDataSource("DataSets_SiteEval", objDT))
                End Using
            Case ....

C# version:

switch (GetReportName()) {

    case "SiteEval":
        using (DataSetsTableAdapters.SiteEvalTableAdapter adp = new DataSetsTableAdapters.SiteEvalTableAdapter()) {
            using (DataSets.SiteEvalDataTable objDT = new DataSets.SiteEvalDataTable()) {
                adp.Fill(objDT);
                objLR.DataSources.Add(new ReportDataSource("DataSets_SiteEval", objDT));
            }
        }

        break;

Leveraging our XSD's and datasets to get our reports loading. In this case our local reports have access to the DB. In your case you could take this idea and fill the dataset with your own returned datatable from a call into your service. It's not the most elegant or easiest to maintain if you have a large set of reports.

In our case, we are now taking the RDL Viewer example and modifying it to our needs so that the above code is not needed. We will just pass in the path to the RDLC file and the code will load what is needed by reading the RDLC's xml. However, in this case the local report has access to the DB. It would not be to difficult I think to modify it to get it's data from an outside source either using the above code idea or modifying the RDL Viewer example from www.gotreportviewer.com as we are now doing.

With the RDL Viewer example modified we have a little something like this going at the moment (still working it out...) Code is in VB.

    Dim r As New Report(Server.MapPath("App_Reports/" & GetReportName() & ".rdlc"), GetReportName())

    Dim p As ReportParameterInfoCollection = r.GetParameters() 'read only....
    If p.Count > 0 Then
        Dim rptParams(p.Count - 1) As ReportParameter
        Dim i As Integer = 0
        For Each param In p
            rptParams(i) = New ReportParameter(param.Name)
            rptParams(i).Values.Add("99999999")
            i += 1
        Next
        r.SetParameters(rptParams)
    End If

    r.LoadReport()

Goodbye ugly and long switch statement. Goodbye needing to know how to load the report.... Now if we can just figure out how to handle parameters a little nicer.... This code is a current work in progress but the RDL Viewer sample got us started quickly....

I also am playing with doing the same thing with Reflection. I found this article that makes the reflection work a breeze. http://www.slimee.com/2009/09/net-using-reflection-to-execute.html

Now all you would do is pass in the string of your data table and it will produce the datatable driving the report. Since with datasets you will always know how it creates the names you can easily work with this.

The way I see it is both approaches are solutions that work at eliminating the ugly switch statements and will make the code easier to maintain. With reflection the code is much smaller but may be a bit slower.

Both have a bit of the magic string issue in them. Somewhere along the line you have to pass in a string of what you want to run. A team could easily create a convention that to easily solve though....

klabranche
That one example looks very useful. I will have to do some more research on the RDL schema so I can see how "conditional" parameters are represented. As to the "hints," I am leery of using some huge switch statement and would rather do things more generically. I suppose I could inspect the RDL to get the queries out and just execute them and send back the resulting datasets.
Matthew Belk
klabranche
I added how to do this with reflection now also.... Not sure which way we will go with but the reflection sample is pretty concise and we won't have issues with speed and/or trust issues.
klabranche