Data binding establishes a connection between a report and the data it displays. ActiveReports.NET allows you to bind data at design time, but there are times when you need to bind data at run time, for example:

  • You use a database, such as MongoDB, that is not supported for binding at design time.
  • Your application has an existing data layer implementation that you want to re-use for report data binding.

To support these scenarios, ActiveReports.NET provides the ability to bind data at runtime. This article suggests a simple framework for designing reports using mock data at design time and replacing them with the actual data at runtime. The described procedures are only applicable for RDL and Page Reports.

Get ActiveReports .NET reporting solution

Download the latest version of ActiveReports Professional

Download Now!

Use Case

Suppose you are working on a call center software that provides a monthly performance report to a manager. For example, the manager needs to know the number of calls received by each employee, the number of answered calls, the average call duration, and the resolution rate. The UX team provided the report design, including colors, fonts, and formatting. Here is the example of such a report:

image1

The application's data layer allows to query the data source for call records obtained from a given period, and the result is the list of objects like the following:

public class CallRecord
{
    /// <summary>
    /// Gets or sets the start date of the call
    /// </summary>
    public DateTime StartDate { get; set; }
    /// <summary>
    /// Gets or sets duration of the call in seconds
    /// </summary>
    public float Duration { get; set; }
    /// <summary>
    /// Gets or sets the name of agent handling the call
    /// </summary>
    public string AgentName { get; set; }
    /// <summary>
    /// Gets or sets the value indicating whether the call was answered
    /// </summary>
    public bool Answered { get; set; }       
    /// <summary>
    /// Gets or sets the value indicating whether the call was resolved
    /// </summary>
    public bool Resolved { get; set; }
}

The data layer interface is as follows:

public interface IDataLayer
{
    IEnumerable<CallRecord> GetPerformanceData(DateTime startDate, DateTime endDate);
}

Designing a report

First off, you need to design a report according to the UX guidelines. To do that, you don't need to access the actual data. It is convenient to use mock data so that you can quickly switch between the report design and its output in the Visual Studio Integrated Designer without having to run your application to generate the report output.

ActiveReports.NET supports the CSV data provider that you can use to bind a report to mock data. To generate simulated data, you can use any available CSV data generator, for example, the one from extendclass. Once the data are ready, you can save them into a local file. For example, the following snippet shows the fragment of such a file.

StartDate

Duration
AgentName
Answered
Resolved
2021-01-28 110.68 Greg False True
2021-01-22 15.14 Jim True True
2021-01-28 20.17 Jim False True
2021-01-20 44.8 Martha False False

To create a new report that is bound to the mock data, follow these steps:

  1. In Visual Studio, open your project.
  2. Add a new ActiveReports 15 RDL Report or ActiveReports 15 Page Report to the project.
  3. Open the Report Explorer.
  4. Add a new Data Source, select the CSV Provider in the Type dropdown of the data source dialog, and click the Edit icon in the toolbar of the Connection String editor.
  5. Click the Open button and choose the CSV file containing the mock data.
  6. Click the Get from Preview button. The dialog populates the column list automatically.
  7. Set the data type for each column and click the OK button.

image2

The report designer will automatically create the new DataSet and populate its fields. So now you can design the report, check its output in the Preview tab, get back to the Design tab, adjust if it's needed, and so on.

Supplying the actual data at runtime

In the actual application, the data comes from the data layer that returns the CallRecord object list. ActiveReports.NET offers the Object data provider that accepts any object that implements the IEnumerable interface as the input. So, the first thing you need to do is modify the report's data source type.

  1. In the Visual Studio make sure that the report is opened.
  2. In the Report Explorer select the data source item and click the Edit button.
  3. Select Object Provider in the Type dropdown and click the OK button.
  4. Save the changes.

If you preview the report in the Preview tab, you will see that it shows the error message:

GrapeCity.ActiveReports.ReportException: An unexpected error occurred. Additional information: 'No data has been set. Please specify IEnumerable to use

The application code should provide the data for the Object data provider in the LocateDataSource event handler. For example, the following code loads the report definition from the application resources, adds the LocateDataSource event handler, and exports the report output to the PDF document saving it in the memory.

internal System.IO.Stream GenerateReportOutput(IDataLayer dataLayer, System.DateTime startDate, System.DateTime endDate)
{
    using (var reportStream =GetType().Assembly.GetManifestResourceStream("RuntimeDataBinding.CallCenterPerformace.rdlx"))
    using (var reader = new System.IO.StreamReader(reportStream))
    {
        var rpt = new GrapeCity.ActiveReports.PageReport(reader);
        var pdfRe = new GrapeCity.ActiveReports.Export.Pdf.Page.PdfRenderingExtension();
        var output = new GrapeCity.ActiveReports.Rendering.IO.MemoryStreamProvider();
        rpt.Document.LocateDataSource += (sender, args) =>
        {
            args.Data = dataLayer.GetPerformanceData(startDate, endDate);
        };
        rpt.Document.Render(pdfRe, output);
        return output.GetPrimaryStream().OpenStream();
    }
}

LocateDataSource event

The LocateDataSource event is the foundation of the runtime data binding. The event handler is called by the report engine when the report needs to obtain the data. The event handler should set the Data property of the LocateDataSourceEventArgs instance to the data object that the report will use as the data source. Several data providers support runtime data binding. The following table shows the list of these data providers along with the acceptable data types and conditions for LocateDataSource occurrence.

Data Provider

LocateDataSource event occurs
Acceptable Data Types
OBJECT Always IEnumerable
XML ConnectionString of the Data Source is empty XmlReader, XmlDocument, IXPathNavigable
DATASET Always DataTable, DataView, DataSet
CSV ConnectionString of the Data Source is empty String containing CSV data
JSON ConnectionString of the Data Source is empty String containing JSON data

The LocateDataSourceEventArgs instance that is passed to the event handler also exposes the following useful properties when the same LocateDataSource handler is called for several reports. For instance, if a report contains a subreport, then the LocateDataSource event handler will be called for each subreport instance in addition to the main report.

Property

Description
Parameters The list of Report DataSet parameters, including Name and Value.
DataSet The Report Data Set that the data are requested for.
Report The Report definition that the data are requested for.

Finally, if you use the JSViewer and want to supply the data at runtime, then the LocateDataSource event handler is set on for the UseReporting middleware, for example:

app.UseReporting(settings =>
{
    settings.SetLocateDataSource((args) =>
    {
        var startDate = (DateTime)args.Parameters[0].Value;
        var endDate = (DateTime)args.Parameters[1].Value;
        return _dataLayer.GetPerformanceData(startDate, endDate);
    });
});

Get ActiveReports .NET reporting solution

Download the latest version of ActiveReports Professional

Download Now!