Categories
C# Telerik

Loading a Telerik Report in .Net Core 3.1

In intend to demonstrate embedding a Telerik report in a .Net Core 3.1 AspNetCore application in this post. It will follow a simple formula. Set up the startup class, write the report controller, add the report viewer, GO! The complete code for this application can be found on github here.

Some things you need:

  • NuGet package: Microsoft.AspNetCore.Mvc.NewtonsoftJson
  • Nuget package: Telerik.Reporting.Services.AspNetCore (this one is on the private nuget feed at https://nuget.telerik.com/nuget , you’ll need a logon)
    • alternatively you should be able to find those two assemblies in your Telerik Reporting install bin folder. Something like here: C:\Program Files (x86)\Progress\Telerik Reporting R3 2019\Bin
  • NuGet package: Telerik.Reporting (it’s free but reports can only be generated using the report generator installed in the paid product.
  • A report definition. (if you’re reading this, you probably already have it, if not…free trial baby!)

There is a quick example I put together for myself in the repository but it obviously won’t function if you don’t have access to my local database. You will need to drop in your own or use an example from Telerik. I’m not sure about rights on the license so I didn’t want to share their samples.

Now to the real stuff…

First things first, spin up a new .Net Core 3.1 AspNetCore application. I go MVC by default. Then add all the NuGet packages mentioned above, and move the report viewer javascript files into your wwwroot/js folder.

We’re gonna use a custom configuration service for easy access to the reports folder on disk. Call it ConfigurationService

Configuration

    public class ConfigurationService
    {
        public IConfiguration Configuration { get; private set; }
        public IWebHostEnvironment Environment { get; private set; }
        public ConfigurationService(IWebHostEnvironment environment, IConfiguration config)
        {
            this.Environment = environment;
            this.Configuration = config;
        }
    }

Telerik reporting still leverages Newtonsoft Json, so we have to add that compatibility back with the NewtonsoftJson package mentioned above. Once installed, you can call this in you ConfigureServices method:

            services.AddMvc()
                .AddNewtonsoftJson();

or
            services.AddRazorPages()
                .AddNewtonsoftJson();

While you’re in there add 

services.Configure<IISServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });

and we need to store the service collection so add a private field to the class

private IServiceCollection services;

Then store that value in the ConfigureServices method with.

this.services = services;

Then we can add the ConfigurationService in the Configure method, which allows us to inject the IWebHostEnvironment variable into the controller we’ll write next. We also need to make sure that we map the endpoints, this can add some trickiness later on depending on where you are placing your reports, but this is a functional demo so nothing fancy just yet.

     this.services.AddTransient(ctx => new Controllers.ReportsController(new ConfigurationService(env, Configuration)));

....other code .....

    app.UseEndpoints(endpoints =>
    {
         endpoints.MapDefaultControllerRoute();
    });

Controller

Next we need a report controller. I’ll call it ReportsController…obvious enough for me. It’s not going to do anything fancy really, just get us a list of reports. Note specifically the use of the ReportsControllerBase class that requires an import of the Telerik reporting libraries.

    [Route("api/reports")]
    public class ReportsController : ReportsControllerBase
    {
        readonly string reportsPath = string.Empty;
        public ReportsController(ConfigurationService configSvc)
        {
            this.reportsPath = Path.Combine(configSvc.Environment.WebRootPath, "Reports");
            this.ReportServiceConfiguration = new ReportServiceConfiguration
            {
                ReportingEngineConfiguration = configSvc.Configuration,
                HostAppId = "AspNetCoreReportDemo",
                Storage = new FileStorage(),
                ReportResolver = new ReportTypeResolver()
                                    .AddFallbackResolver(new ReportFileResolver(this.reportsPath)),
            };
        }
        [HttpGet("reportlist")]
        public IEnumerable<string> GetReports()
        {
            return Directory
                .GetFiles(this.reportsPath)
                .Select(path =>
                    Path.GetFileName(path));
        }
    }

View and the ReportViewer

At this point it’s a good idea to make sure you’ve got that report you wrote in the correct place. Put it in a new Reports folder directly under the wwwroot folder. See that reportsPath call in the last code entry? This is the folder where that is going to look at runtime.

Now we can actually call the sucker in a view. But first we’ll need to make sure we get all the styles and such to the browser. Add a few items to the head tag of the _layout.cshtml shared view. 

    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <link href="https://kendo.cdn.telerik.com/2019.1.115/styles/kendo.common.min.css" rel="stylesheet" />
    <link href="https://kendo.cdn.telerik.com/2019.1.115/styles/kendo.blueopal.min.css" rel="stylesheet" />    
    <script src="/api/reports/resources/js/telerikReportViewer"></script>
    <style>
        #reportViewer1 {
            position: absolute;
            left: 5px;
            right: 5px;
            top: 50px;
            bottom: 5px;
            overflow: hidden;
            font-family: Verdana, Arial;
        }
    </style>

Note the script above points to a script that doesn’t exist within your project. That is by design. The src actually hits an endpoint exposed by the ReportsControllerBase class that is inherited by the previously written ReportsController. It is not a script you will be able to see. Telerik reporting also uses a specific jquery version, so you’ll want to comment out the jquery script tag that comes after the footer by default, it will cause nothing to load if it remains in place.


Then we can move on to the Index view of our application, where we’ll jam in tour example report. replace the html there with this.

<div id="reportViewer1">
    loading...
</div>
@section Scripts {
    <script>
        $(document).ready(function () {
            $("#reportViewer1")
                .telerik_ReportViewer({
                    serviceUrl: "../../api/reports/",
                    reportSource: {
                        report: "ExampleReport.trdp",
                        parameters: {}
                    },
                    viewMode: telerikReportViewer.ViewModes.INTERACTIVE,
                    scaleMode: telerikReportViewer.ScaleModes.SPECIFIC,
                    scale: 1.0,
                    enableAccessibility: false,
                    sendEmail: { enabled: true }
                });
        });
    </script>
}

Not a particularly tough bit of code, but the conventions and some missing points in the Telerik documentation caused this to be tougher to get running that anticipated. I do hope this sample will help someone get up and running in short order. 

Cheers

By Jim Pusateri

Software Developer and Scientist

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s