New to Telerik ReportingStart a free 30-day trial

Implementing a Custom ReportSource Resolver Explained with Example

This article explains how to create a custom report source resolver for the Telerik Reporting REST Service. In this example, the resolver's purpose is to return an XmlReportSource with an XML report definition obtained from a SQL Server database.

The report sources passed from the Report Viewers using Reporting REST Service and Navigate To Report action are resolved by the ReportSource Resolver of the service. The ReportSource of the SubReport item is resolved in the context of the Reporting Engine and doesn't use the ReportSource Resolver of the REST Service.

Use the ReportDocument Resolver to resolve them with custom code as explained in Implementing Custom ReportSource and ReportDocument Resolvers To Fully Control the Report in Run Time.

  1. Create a class that implements the IReportSourceResolver interface. Its Resolve method is called whenever the engine needs to create a ReportSource instance based on the parameter named report. The value of the report parameter is initialized with the value of the Report property of the report viewer's ReportSource object.

    C#
    class CustomReportSourceResolver : IReportSourceResolver
    {
        public Telerik.Reporting.ReportSource Resolve(string reportId, OperationOrigin operationOrigin, IDictionary<string, object> currentParameterValues)
        {
            var cmdText = "SELECT Definition FROM Reports WHERE ID = @ReportId";
    
            var reportXml = string.Empty;
            using (var conn = new System.Data.SqlClient.SqlConnection(@"server=(local)\sqlexpress;database=REPORTS;integrated security=true;"))
            {
                var command = new System.Data.SqlClient.SqlCommand(cmdText, conn);
                command.Parameters.Add("@ReportId", System.Data.SqlDbType.Int);
                command.Parameters["@ReportId"].Value = reportId;
    
                try
                {
                    conn.Open();
                    reportXml = (string)command.ExecuteScalar();
                }
                catch (System.Exception ex)
                {
                    System.Diagnostics.Trace.WriteLine(ex.Message);
                }
            }
    
            if (string.IsNullOrEmpty(reportXml))
            {
                throw new System.Exception("Unable to load a report with the specified ID: " + reportId);
            }
    
            return new Telerik.Reporting.XmlReportSource { Xml = reportXml };
        }
    }
  2. Find the ReportSourceResolver property of the ReportServiceConfiguration, and set it to an instance of the custom report source resolver or to a chain of resolver instances, including the custom one. In the following examples, the REST Service tries to resolve the report in the sequence:

    1. Declarative report definition (TRDP/TRDX/TRBP file), through the UriReportSourceResolver.
    2. Type report definition (CS or VB type inheriting Telerik.Reporting.Report), through the TypeReportSourceResolver.
    3. Custom report identifier, through the CustomReportSourceResolver.
    • In .NET Framework, the ReportServiceConfiguration property is in the implementation of the ReportsControllerBase class:

      C#
      public class CustomResolverReportsController : ReportsControllerBase
      {
          static ReportServiceConfiguration configurationInstance;
      
          static CustomResolverReportsController()
          {
              var resolver = new UriReportSourceResolver(HttpContext.Current.Server.MapPath("~/Reports"))
                  .AddFallbackResolver(new TypeReportSourceResolver()
                      .AddFallbackResolver(new CustomReportSourceResolver()));
      
              configurationInstance = new ReportServiceConfiguration
              {
                  HostAppId = "Application1",
                  ReportSourceResolver = resolver,
                  Storage = new Telerik.Reporting.Cache.File.FileStorage(),
              };
          }
      
          public CustomResolverReportsController()
          {
              this.ReportServiceConfiguration = configurationInstance;
          }
      }
    • In .NET, the ReportServiceConfiguration is usually added as a singleton in the DI container at the starting point of the application:

      C#
      builder.Services.TryAddSingleton<IReportServiceConfiguration>(sp =>
       new ReportServiceConfiguration
       {
           // The default ReportingEngineConfiguration will be initialized from appsettings.json or appsettings.{EnvironmentName}.json:
           ReportingEngineConfiguration = sp.GetService<IConfiguration>(),
      
           HostAppId = "DotNetApplication1",
           Storage = new FileStorage(),
           ReportSourceResolver = new TypeReportSourceResolver()
               .AddFallbackResolver(new UriReportSourceResolver(reportsPath)
                   .AddFallbackResolver(new CustomReportSourceResolver()))
       });
  3. Request the report from the HTML5 Report Viewer on the client:

    HTML
    <script type="text/javascript">
        $(document).ready(function () {
            $("#reportViewer1").telerik_ReportViewer({
                serviceUrl: "api/reports/",
                reportSource: { report: 1 }
            });
        });
    </script>
  4. To create the database, use the following script:

    SQL
    USE [master]
    GO
    CREATE DATABASE [Reports]
    CONTAINMENT = NONE
    ON PRIMARY
    (NAME = N'Reports', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL11.SQLEXPRESS\MSSQL\DATA\Reports.mdf', SIZE = 4096KB, MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB)
    LOG ON
    (NAME = N'Reports_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL11.SQLEXPRESS\MSSQL\DATA\Reports_log.ldf', SIZE = 1024KB, MAXSIZE = 2048GB, FILEGROWTH = 10%)
    GO
    USE [Reports]
    GO
    CREATE TABLE [dbo].[Reports](
    [ID] [int] NOT NULL,
    [Definition] [nvarchar](max) NOT NULL
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    GO
  5. To enter some data into the database, manually edit the Reports table. XML report definitions can be obtained from the sample .trdx report files installed together with the product and located in [Telerik_Reporting_Install_Dir]\Report Designer\Examples. In newer versions, all sample reports of the Standalone Report Designer are in TRDP format. Use the Standalone Report Designer > File > Save As option to convert them to TRDX files.

How to Implement and Use a Custom IReportSourceResolver with a Fallback Mechanism

  1. Add to your IReportSourceResolver implementation a constructor with the parameter IReportSourceResolver parentResolver. Then use the parentResolver if the custom report source resolving mechanism fails.

    C#
    class CustomReportSourceResolverWithFallBack : IReportSourceResolver
    {
        readonly IReportSourceResolver parentResolver;
    
        public CustomReportSourceResolverWithFallBack(IReportSourceResolver parentResolver)
        {
            this.parentResolver = parentResolver;
        }
    
        public ReportSource Resolve(string report, OperationOrigin operationOrigin, IDictionary<string, object> currentParameterValues)
        {
            ReportSource reportDocument = null;
            reportDocument = this.ResolveCustomReportSource(report, operationOrigin, currentParameterValues);
    
            if (null == reportDocument && null != this.parentResolver)
            {
                reportDocument = this.parentResolver.Resolve(report, operationOrigin, currentParameterValues);
            }
    
            return reportDocument;
        }
    
        public ReportSource ResolveCustomReportSource(string report, OperationOrigin operationOrigin, IDictionary<string, object> currentParameterValues)
        {
            //TODO implement custom report resolving mechanism
            return null;
        }
    }
  2. Add to the ReportServiceConfiguration the IReportSourceResolver implementations in a chain. Thus, the custom one is executed first, and if it fails, the second one is invoked, and so on.

    • In .NET Framework, the ReportServiceConfiguration is configured in the implementation of the ReportsControllerBase class:

      C#
      public class CustomResolverWithFallbackReportsController : ReportsControllerBase
      {
          static ReportServiceConfiguration configurationInstance;
      
          static CustomResolverWithFallbackReportsController()
          {
              var resolver = new CustomReportSourceResolverWithFallBack(new TypeReportSourceResolver()
                      .AddFallbackResolver(new UriReportSourceResolver(HttpContext.Current.Server.MapPath("~/Reports"))));
      
              configurationInstance = new ReportServiceConfiguration
              {
                  HostAppId = "Application1",
                  ReportSourceResolver = resolver,
                  Storage = new Telerik.Reporting.Cache.File.FileStorage(),
              };
          }
      
          public CustomResolverWithFallbackReportsController()
          {
              this.ReportServiceConfiguration = configurationInstance;
          }
      }
    • In .NET, the ReportServiceConfiguration is usually added as a singleton in the DI container at the starting point of the application:

      C#
      builder.Services.TryAddSingleton<IReportServiceConfiguration>(sp =>
       new ReportServiceConfiguration
       {
           // The default ReportingEngineConfiguration will be initialized from appsettings.json or appsettings.{EnvironmentName}.json:
           ReportingEngineConfiguration = sp.GetService<IConfiguration>(),
      
           HostAppId = "DotNetApplication1",
           Storage = new FileStorage(),
           ReportSourceResolver = new CustomReportSourceResolverWithFallBack(new TypeReportSourceResolver()
               .AddFallbackResolver(new UriReportSourceResolver(reportsPath)))
       });

    For the fallback, you can use the default IReportSourceResolver implementations:

    • TypeReportSourceResolver—resolves IReportDocument from the assembly-qualified name of a Report or ReportBook class.
    • UriReportSourceResolver—resolves IReportDocument from the physical path to a TRDP, TRDX, or TRBP file.