I've been going through the documentation repeatedly for a few days and I believe that what I'm looking to use is the ReportBook but not sure where to start.
For some insight, we have roughly around 8 reports in total. These reports vary but for the most part, they are individual reports intended for one role to view. However, the problem here is that we need to take 3 of these reports and iterate over selections to create a single report for viewing.
Current Scenario:
A district manager can view reports on locations. Each location has 3 reports that can be viewed independently. Along with this, the district manager can also view an "Overview" report that sums up the data of their locations in a single report. The Overview is very basic and no detail is provided. All reports contain parameters of the location id and a start and end date for gathering data.
Problem Scenario:
The problem now is that we need for the district manager to be able to capture a full detailed report of all selected locations in a single report. In this scenario, the district manager may select from 1 to N amount of locations and a report is generated based on the selections. This generated report will show all 3 reports for each location selected.
I'm not sure where to start with such an implementation and the documentation on ReportBook isn't very detailed.
Tech stack is .NET 6 with Blazor frontend and .NET 6 backend in separate applications.
1 Answer, 1 is accepted
Hello Joshua,
Thank you for the detailed information!
Based on the scenarios, I agree that most likely the ReportBook is what you need to use. The main goal of a report book is combining different reports into one which will allow you to integrate as many "detail" reports as needed in a single "overview" report which will be the ReportBook. To get an idea of how the ReportBook looks and how it works, you may check the ReportBook that we ship with the installation of the product, located at:
C:\Program Files (x86)\Progress\Telerik Reporting R1 2023\Report Designer\Examples
Since the selected locations may vary(from what I understood), you will probably need to generate the ReportBook dynamically or in other words, at runtime. How this can be done is explained in the Instantiating Report Books at run-time - Telerik Reporting article.
Since you will probably also wish to display the report book in a report viewer in your blazor application, I also recommend having a look at the How to display ReportBook in viewer - Telerik Reporting KB article where 90% of this scenario is covered. You will only need to add the logic for passing the locations to the resolver, which may happen via the report viewer.
If you have not used a report viewer yet, I recommend looking into the Native Blazor Report Viewer at a Glance - Telerik Reporting article.
I hope that the provided information will help, please let me know if you have further questions.
Regards,
Dimitar
Progress Telerik
I appreciate your answer but the documentation misses a few things here.
1. Is this supposed to live on frontend with the Blazor Application or backend with REST application? Assumably the rest application.
2. Showing this in the Blazor ReportViewer or the Blazor Native ReportViewer doesn't match other report viewers in regards to passing information in more than 1 format, IE: ReportSourceOptions with string Report name and dictionary parameters.
3. When building the ReportBook in runtime, due to the "CreateInstance" wanting to pass in ClientReportSource, I have no way of even returning this in a proper format to the ReportViewer anyway and HTML5 format fails every single time when I try to use it.
4. Building the ReportBook on backend "works" but with several issues
* I need to pass in a source (we have multiple environments so a single source in a report doesn't work) which means I have to open the report VIA ReportPackgers.Unpackage just to set the proper datasource.
* As mentioned above, HTML5 format just outright does not work. It times out every time but PDF, PPTX, DOCX, etc works fine.
* I've only added one of the 3 reports so far and its taking roughly 30-50seconds to generate a report, as is. That is not acceptable. Without even moving forward, I can assume that adding the other 2 reports will triple the amount of time to 90-150 seconds.
The reports need to be near real-time. We are not opposed to caching reports after first generation but it cannot be cached for more than 5-10 minutes at a time.
Perhaps the way I'm returning the reportbook is the problem as well? Currently, after adding the reports in dynamically, I perform a ReportProcessor.RenderReport() function. As stated before, I'm not sure how else to return this if it isn't in the format that the CreateInstance function of the ReportController requires?
Perhaps the reportbook isn't the way to go here?
I am sorry to hear that you are still experiencing issues.
1. Is this supposed to live on frontend with the Blazor Application or backend with REST application?
All ReportBook-related code should be used on the backend, inside the custom implementation of the IReportSourceResolver interface specifically.
2. Showing this in the Blazor ReportViewer or the Blazor Native ReportViewer doesn't match other report viewers in regards to passing information in more than 1 format, IE: ReportSourceOptions with string Report name and dictionary parameters.
This ReportSource wrapper format is used for all web-based report viewers which include the Blazor Wrapper and Blazor Native ones. Only the desktop report viewer such as WPF, WinForms, and WinUI use the Telerik.Reporting.ReportSource ancestors directly. The exception for the web report viewers is only the MVC Report Viewer which can also be passed such objects. All the others use a general ReportSource wrapper object that is later resolved by the ReportSourceResolver to the correct type of Telerik.Reporting.ReportSource.
3. When building the ReportBook in runtime, due to the "CreateInstance" wanting to pass in ClientReportSource, I have no way of even returning this in a proper format to the ReportViewer anyway and HTML5 format fails every single time when I try to use it.
Based on this explanation, I assume that you are trying to override the CreateInstance(String, ClientReportSource) method of the ReportsController with the custom logic regarding the ReportBook. This is not the correct place to do so and that is why you have run into problems. I will discuss in the next point where the ReportBook code should be executed.
4. Building the ReportBook on backend "works" but with several issues
On the subpoints that you have mentioned here, I infer that you are able to render the report to some formats using the ReportProcessor class. This approach needs a few adjustments for HTML5 rendering because it is a multi-document format. You can refer to Exporting a report to a multi document format for code examples of how that is done.
With the above being said, if your intentions are to display the report in the report viewer, the above approach will not work for your needs. When it comes to web report viewers, each report to be viewed is handled by the ReportServiceConfiguration.ReportSourceResolver.
All resolvers implement the IReportSourceResolver interface and use the Resolve method to return some type of Telerik.Reporting.ReportSource object. What you need to do in order to display your dynamically created ReportBook is to implement the interface in your own class and to have the ReportBook created in the Resolve method. At the end of that method, the ReportBook has to be wrapped in an InstanceReportSource and returned. All of those steps are demonstrated in the 3rd solution of the How to display ReportBook in viewer - Telerik Reporting KB article.
For more general guidance on implementing a custom resolver, please refer to the Using Custom ReportSource Resolver and Custom ReportDocument Resolver - Telerik Reporting article.
Report Performance
There are a lot of factors that play a part in the rendering time of a report such as the report structure, the amount of data, the machine's capabilities, etc.
I recommend visiting the Telerik reporting RAM usage in Reporting | Telerik Forums thread where this is discussed in detail, including suggestions on how to improve the performance.
::EDIT
I'm able to pull the report after a lot of very careful breakpoints and stepping through data. However, when pulling data from the datasource (DB) from the reports individually and with only 1 report, now at 72 seconds. When running data in parallel and updating the datasource for the reports as an ObjectDataSource, now down to 50-60 seconds.
I've noticed that these processes on the report viewer take roughly 5-6 seconds to complete (acceptable):
- parameters
- instances
- documents
Then it polls on info for the remainder of the time which leaves me to assume that the rendering is what is slow.
I am glad to hear that the report is now working!
Indeed, the multiple Get Document Info requests, indicate that the document processing/rendering is still in progress. Did you have a look at the Performance Factors at a Glance - Telerik Reporting article? If not please do so, and try to implement the suggestion mentioned there.
I did take a look and ultimately decided to optimize the data and report.
We found that trying to handle more than one datasource inside of a report was causing a number of issues when generating the reportbook at runtime. Another problem we had was adding a second, different report to the report book as it would cause an index out of range error.
The multiple data sources were brought together into a single stored procedure to collect all data needed. A new report was created as a "master" so that we only needed to modify the single report. From this, we created groups within this report and added our consolidated data from the other reports we wished to include. In the groups, we were able to add the PageBreak as well as the DocumentMapText and TocText which made for a nice viewing experience in the viewer.
The single data source was pulled in runtime and added to the master report as an ObjectDataSource. Inside of the master report, each table, list, or associated area was binded to the specific property associated within the object passed in from the ObjectDataSource. This allowed better control over what is being delivered where.
LESSONS LEARNED
It is easier to setup a master report than it is to modify information at runtime. I would HIGHLY advise anyone else looking to perform the same functionality to do it this way.
There appears to be multiple areas where either documentation couldn't be found or it appeared to be assumed that the developer knew how things worked.
1. To iterate over multiple tables/subreports, add a list and bind it to the data necessary. You can then bind non-iterative data by a simple Fields.{PropertyName} and bind internal table or list data to ReportItem.DataObject to get the data from the list parent.
The DocumentMapText is what is shown in the viewer. I found no indication in the Documentation on how to set up a "treeview" on the DocumentMap and stumbled across adding text here. For anyone looking, this is where it is accomplished. As a bonus, I was also able to bind to the Panel within the iterated List with the =Fields.{Property} to show this specific name in the DocumentMap.
While I do enjoy the value of all Telerik components and software, the documentation can be difficult to work around. Most older posts are gone and the examples are unhelpful in scenarios like the one I encountered.
Could documentation please be added to explain the following for future users:
- Document Map and how to use
- Importance of single data source
- Full example of dynamic report book creation
- Where, how, and why to Bind to a data source.
Thank you for the feedback!
In regard to the points listed at the end, we will have those in mind when working on the documentation revamp. However, some of the points already have documentation for them and it would be useful if concrete feedback for how they can be improved was provided.
For example, regarding the Document Map, there is the Document Map in Detail - Telerik Reporting article. What kind of additional information would you like added to it? You can submit direct feedback through the form("Is this article helpful?") shown on the screen.
The ReportBook dynamic creation also has the Instantiating Report Books at run-time - Telerik Reporting and How to display ReportBook in viewer - Telerik Reporting articles which do have examples in them though I do agree that they aren't full examples. We will try to add a sample project to the telerik/reporting-samples (github.com) repo with dynamic report book initialization and will link it in the articles.
Regarding the importance of a single data source, a lot of the stuff that you have pointed out will be useful to anyone looking to design reports, however, I also have to mention that having a single data source is not always needed/necessary. Generally, a single data source comes in handy when one needs to group his data at the top level and to have the grouped data usable across the whole report but most of the time, there are multiple approaches for creating a report to achieve the same result, it all depends on the case.
Still, for anyone that wishes to optimize how data is used in the report, the Best Practices for Data Retrieval in Telerik Reporting blog post is a good starting point.
When it comes to data binding, have you checked out the Binding to Data Overview - Telerik Reporting or Using Parent DataItem Collection Property as Child DataItem DataSource - Telerik Reporting articles? Most often, the scenario for binding a data source to a field is due to needing to access nested data, and the way to do it is described in those articles.
If you have seen those articles but still had issues, please use the feedback form in the article to directly suggest what to improve. This way it is easier to associate a comment to an article. If you have not seen those articles, then we would need to make them easier to access but this is already a part of the documentation revamp initiative that we will be working on this year.