Reporting Data Source Factory
  • 14 Oct 2022
  • 3 Minutes to read
  • Dark
    Light

Reporting Data Source Factory

  • Dark
    Light

Article Summary

Version 7.x .NET Architecture Change

Overview

Creating custom data sources for reports is useful when knowing what all of the data sources will be. When the list of data sources can only be known at runtime, creating a data source factory can create the necessary list of data sources at runtime is useful. The Decisions SDK provides classes that help developers create data source factories. An example of doing that is detailed below.

Review the documentation on creating a Custom Reporting Data Source if necessary, as it describes the type of data source that the data source factory will provide for Decisions.

A Simple Data Source Factory

Before creating a data source factory class, create the class representing the various data sources the factory will produce. For example, let's examine a case where each data source reads its information from a database table.

TableDataSource

The class below is a Data Source that takes a database table name as a parameter in its constructor. Based on that, the class returns the columns it wants to provide reports in the ReportFields property and the Report data from the GetData() method.

Notice that this class has the [Writable] attribute applied to it. This is required when a data source is used by a data source factory. Because a data source factory will be registering instances of this data source class, the [AutoRegisterReportElement] attribute mentioned in the Custom Reporting Data Source example is not used.

using System.Data;
using DecisionsFramework.Design.ConfigurationStorage.Attributes;
using DecisionsFramework.Design.Report;

namespace CustomSDKExamples
{
   [Writable]
   public class CustomReportDataSource : AbstractCustomDataSource, ISpecifiedCategoryReportFilter
   {
       public string TableName { get; set; }

       public CustomReportDataSource(string tableName)
       {
           TableName = tableName;
       }

       public override bool Applies(ReportDefinition definition)
       {
           return definition.HasDataSourcesOrFilters() == false;
       }

       public override ReportFieldData[] ReportFields
       {
           get
           {
               // Here, the class could access the database to discover what the columns are.
               // To simplify this example, we will return two columns of data.
               return new ReportFieldData[]
               {
                   new ReportFieldData(TableName, "Column1", typeof(int)),
                   new ReportFieldData(TableName, "Column2", typeof(string))
               };
           }
       }

       public override DataTable GetData(DataTable table, IReportFilter[] filters, int? limitCount, int pageIndex)
       {
           // create the DataTable if it doesn't exist yet
           if (table == null) table = new DataTable();

           // set up the columns in the DataTable based on the ReportFields
           table.Columns.AddRange(GetColumnsFromReportFields(ReportFields));

           // At this point, the class would read the rows from the database and fill the DataTable
           // [CODE OMITTED]

           return table;
       }

       // This property implemented as part of ISpecifiedCategoryReportFilter,
       // allows the class to specify which category it belongs in.
       public string[] Category
       {
           // For this example, specify a sample category name.
           get { return new string[] {"My Database Table Sources"}; }
       }

       // When used with a Data Source Factory, Data Sources have their names pulled from the ToString() method.
       public override string ToString()
       {
           return TableName;
       }
   }
}

TableDataSourceFactory

Now a data source factory can be created, which creates objects of the TableDataSource class and returns them. 

The [AutoRegisterReportElementFactory] attribute tells Decisions that this class is a factory that provides Reporting Data Sources. Decisions will call the GetFilters() method to retrieve the data sources and make them available in the selection list for Report designers to pick from. After building these two classes in a project with the Decisions SDK and installing it (for details, see Creating a Module), the two TableDataSources appear:

using System.Collections.Generic;
using DecisionsFramework.Data.ORMapper;
using DecisionsFramework.Design.Report;

namespace CustomSDKExamples
{
   [AutoRegisterReportElementFactory("My Database Table Sources", "Sample Factory")]
   public class CustomReportDataSourceFactory : IFilterFactory
   {
       // This method returns the data sources that Decisions should use.
       public IReportFilter[] GetFilters(CompositeSelectStatement compositeSelectStatement, IReportFilter[] currentFilters)
       {
           List<CustomReportDataSource> results = new List<CustomReportDataSource>();

           // Here, the class would determine which database tables should be made available as data sources
           // based on whatever logic is required.
           // As an example, two tables are used as parameters for TableDataSource objects.
           results.Add(new CustomReportDataSource("dog"));
           results.Add(new CustomReportDataSource("cat"));

           return results.ToArray();
       }

       // This method is part of the IFilterFactory interface, but not necessary for this example.
       // DataSources that implement the ICustomFilter also implement an IncludeRow(DataRow row) method,
       // which allows them to filter out individual rows from the report.
       public ICustomFilter[] GetCustomFilters(ReportDefinition def, IReportFilter[] currentFilters)
       {
           return null;
       }
   }
}



Was this article helpful?