Base solution for your next web application
Open Closed

Telerik reporting with ASP Net Zero Core jQuery #8424


User avatar
0
Mitch created

Has anyone successfully managed to get Telerik reporting working with ASP Net Zero Core jQuery?


9 Answer(s)
  • User Avatar
    0
    BobIngham created

    I managed to get it working with angular. Where does the problem lie?

  • User Avatar
    0
    Mitch created

    Thanks for responding Bob, I was concerned I might be the only person trying to use Telerik Reporting with ANZ.

    I've been using Telerik Reporting successfully over the past 5 years without any issues in my other projects, but after following the normal Telerik Reporting installation procedures it appears that the reports service is not runnning correctly.

    As I'm using Core 3.1, I'm following these two links to get the app up and running

    https://docs.telerik.com/reporting/telerik-reporting-rest-service-aspnetcore-mvc-core3 https://docs.telerik.com/reporting/manual-setup-of-html5-report-viewer-in-asp-net-core-3

    In the code that sets up the Reports Service, I have used the following in a non ANZ test project to ensure it works :-

    namespace AspNetCoreDemo.Controllers
    {
       using Microsoft.AspNetCore.Mvc;
       using System.Net;
       using System.Net.Mail;
       using Telerik.Reporting.Services;
       using Telerik.Reporting.Services.AspNetCore;
    
       [Route("api/reports")]
       public class ReportsController : ReportsControllerBase
       {
           public ReportsController(IReportServiceConfiguration reportServiceConfiguration)
               : base(reportServiceConfiguration)
           {
           }
       }
    }
    

    I created a basic ASP.Net Core 3.1 App to test the Reporting and it works correctly, so when I navigate to URL {applicationRoot}/api/reports/format, I get

    [{"name":"PDF","localizedName":"Acrobat (PDF) file"},{"name":"CSV","localizedName":"CSV (comma delimited)"},{"name":"XLS","localizedName":"Excel 97-2003"},{"name":"RTF","localizedName":"Rich Text Format"},{"name":"IMAGE","localizedName":"TIFF file"},{"name":"MHTML","localizedName":"Web Archive"},{"name":"XPS","localizedName":"XPS Document"}]
    

    However, If I repeat the installion approach above on ANZ it returns a 404 error when I navigate to URL {applicationRoot}/api/reports/format.

    If I remove /format and navigate to URL {applicationRoot}/api/reports instead, I get

    {"message":"An error has occurred.","exceptionMessage":"No component for supporting the service DpmsCloud.Web.Areas.App.Controllers.ReportsController was found","exceptionType":"Castle.MicroKernel.ComponentNotFoundException","stackTrace":"   at Castle.MicroKernel.DefaultKernel.Castle.MicroKernel.IKernelInternal.Resolve(Type service, Arguments arguments, IReleasePolicy policy, Boolean ignoreParentContext)\r\n   at Castle.MicroKernel.DefaultKernel.Resolve(Type service, Arguments arguments)\r\n   at Castle.Windsor.MsDependencyInjection.ScopedWindsorServiceProvider.GetServiceInternal(Type serviceType, Boolean isOptional)\r\n   at Microsoft.AspNetCore.Mvc.Controllers.ServiceBasedControllerActivator.Create(ControllerContext actionContext)\r\n   at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)"}
    

    I'm guessing that I have to follow a slightly different path to install Telerik Reporting for ANZ, but I'm not sure what needs changing.

    Incidently, I had originally had issues with Telerik Controls not returning data correctly until I decorated the Controllers with [DontWrapResult], so I have experienced ANZ interferring with my normal installation practices before.

    Once again, many thanks for offering to help Bob.

  • User Avatar
    0
    BobIngham created

    Hi @mitch, I'm getting too old for this game, I usually document everything but I can see from my notes that this was a difficult process of trial and error. Here's my notes:

    In project Nuagecare.Web.Host add the following Nuget packages from the “Telerik Nuget” package course: • Telerik.Reporting.Services.AspNetCore • Telerik.Reporting Add new controller, KendoReportsController to Nuagecare.Web.Host following the instructions at: • https://docs.telerik.com/reporting/html5-report-viewer-asp-net-core-2#setting-up-the-rest-service and • https://www.telerik.com/blogs/telerik-reporting-and-aspnet-core And the code for my controller is below.

    using Abp.Authorization;
    using Abp.Dependency;
    using Abp.Web.Models;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Configuration;
    using System;
    using System.IO;
    using Telerik.Reporting;
    using Telerik.Reporting.Cache.File;
    using Telerik.Reporting.Services;
    using Telerik.Reporting.Services.AspNetCore;
    using Telerik.Reporting.Services.Engine;
    
    namespace Nuagecare.Web.Host.Controllers
    {
        [ApiExplorerSettings(IgnoreApi = true)]
        [AbpAuthorize]
        [DontWrapResult]
        public class KendoReportsController : ReportsControllerBase, ITransientDependency
        {
            public KendoReportsController(IHostingEnvironment environment)
            {
                var reportsPath = Path.Combine(environment.WebRootPath, "Reports");
    
                try
                {
                    this.ReportServiceConfiguration = new ReportServiceConfiguration
                    {
                        HostAppId = "Nuagecare",
                        Storage = new FileStorage(),
                        ReportResolver = new NuageResolver(reportsPath),
                        ReportSharingTimeout = 0,
                        ClientSessionTimeout = 15,
                    };
                }
                catch (System.Exception ex)
                {
                    throw ex;
                }
            }
        }
    
        //https://www.telerik.com/support/kb/reporting/details/changing-the-connection-string-dynamically-according-to-runtime-data
        //https://www.telerik.com/forums/change-connectionstring-in-runtime as an alternative
        public class NuageResolver : IReportResolver
        {
            public string repositoryDirectory { get; set; }
    
            public NuageResolver(string repositoryDirectory)
            {
                this.repositoryDirectory = repositoryDirectory;
            }
    
            //this method will be called on each request for report (refresh, navigate to report, sub report, parameters updates)
            //the method will be called 3 times on initial load
            public ReportSource Resolve(string report)
            {
                var connectionStringHandler = new ReportConnectionStringManager(GetConnectionString());
                var sourceReportSource = new UriReportSource { Uri = this.repositoryDirectory + "/" + report };
                var reportSource = connectionStringHandler.UpdateReportSource(sourceReportSource);
                return reportSource;
            }
    
            private string GetConnectionString()
            {
                var env = AppDomain.CurrentDomain.GetData("HostingEnvironment") as IHostingEnvironment;
                string currentDir = Directory.GetCurrentDirectory();
    
                var builder = new ConfigurationBuilder()
                    .SetBasePath(currentDir)
                    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
    
                var configuration = builder.Build();
                string connectionString = configuration.GetConnectionString("Default");
                return connectionString;
            }
        }
    
        //https://www.telerik.com/support/kb/reporting/details/changing-the-connection-string-dynamically-according-to-runtime-data
        public class ReportConnectionStringManager
        {
            readonly string connectionString;
    
            public ReportConnectionStringManager(string connectionString)
            {
                this.connectionString = connectionString;
            }
    
            public ReportSource UpdateReportSource(ReportSource sourceReportSource)
            {
                if (sourceReportSource is UriReportSource)
                {
                    var uriReportSource = (UriReportSource)sourceReportSource;
                    // unpackage TRDP report
                    // http://docs.telerik.com/reporting/report-packaging-trdp#unpackaging
                    var reportInstance = UnpackageReport(uriReportSource);
                    // or deserialize TRDX report(legacy format)
                    // http://docs.telerik.com/reporting/programmatic-xml-serialization#deserialize-report-definition-from-xml-file
                    // var reportInstance = DeserializeReport(uriReportSource);
                    ValidateReportSource(uriReportSource.Uri);
                    this.SetConnectionString(reportInstance);
                    return CreateInstanceReportSource(reportInstance, uriReportSource);
                }
    
                if (sourceReportSource is XmlReportSource)
                {
                    var xml = (XmlReportSource)sourceReportSource;
                    ValidateReportSource(xml.Xml);
                    var reportInstance = this.DeserializeReport(xml);
                    this.SetConnectionString(reportInstance);
                    return CreateInstanceReportSource(reportInstance, xml);
                }
    
                if (sourceReportSource is InstanceReportSource)
                {
                    var instanceReportSource = (InstanceReportSource)sourceReportSource;
                    this.SetConnectionString((ReportItemBase)instanceReportSource.ReportDocument);
                    return instanceReportSource;
                }
    
                if (sourceReportSource is TypeReportSource)
                {
                    var typeReportSource = (TypeReportSource)sourceReportSource;
                    var typeName = typeReportSource.TypeName;
                    ValidateReportSource(typeName);
                    var reportType = Type.GetType(typeName);
                    var reportInstance = (Report)Activator.CreateInstance(reportType);
                    this.SetConnectionString((ReportItemBase)reportInstance);
                    return CreateInstanceReportSource(reportInstance, typeReportSource);
                }
    
                throw new NotImplementedException("Handler for the used ReportSource type is not implemented.");
            }
    
            ReportSource CreateInstanceReportSource(IReportDocument report, ReportSource originalReportSource)
            {
                var instanceReportSource = new InstanceReportSource { ReportDocument = report };
                instanceReportSource.Parameters.AddRange(originalReportSource.Parameters);
                return instanceReportSource;
            }
    
            void ValidateReportSource(string value)
            {
                if (value.Trim().StartsWith("="))
                {
                    throw new InvalidOperationException("Expressions for ReportSource are not supported when changing the connection string dynamically");
                }
            }
    
            Report UnpackageReport(UriReportSource uriReportSource)
            {
                var reportPackager = new ReportPackager();
                using (var sourceStream = System.IO.File.OpenRead(uriReportSource.Uri))
                {
                    var report = (Report)reportPackager.UnpackageDocument(sourceStream);
                    return report;
                }
            }
    
            Report DeserializeReport(UriReportSource uriReportSource)
            {
                var settings = new System.Xml.XmlReaderSettings();
                settings.IgnoreWhitespace = true;
                using (var xmlReader = System.Xml.XmlReader.Create(uriReportSource.Uri, settings))
                {
                    var xmlSerializer = new Telerik.Reporting.XmlSerialization.ReportXmlSerializer();
                    var report = (Telerik.Reporting.Report)xmlSerializer.Deserialize(xmlReader);
                    return report;
                }
            }
    
            Report DeserializeReport(XmlReportSource xmlReportSource)
            {
                var settings = new System.Xml.XmlReaderSettings();
                settings.IgnoreWhitespace = true;
                var textReader = new System.IO.StringReader(xmlReportSource.Xml);
                using (var xmlReader = System.Xml.XmlReader.Create(textReader, settings))
                {
                    var xmlSerializer = new Telerik.Reporting.XmlSerialization.ReportXmlSerializer();
                    var report = (Telerik.Reporting.Report)xmlSerializer.Deserialize(xmlReader);
                    return report;
                }
            }
    
            void SetConnectionString(ReportItemBase reportItemBase)
            {
                if (reportItemBase.Items.Count < 1)
                    return;
    
                if (reportItemBase is Report)
                {
                    var report = (Report)reportItemBase;
    
                    if (report.DataSource is SqlDataSource)
                    {
                        var sqlDataSource = (SqlDataSource)report.DataSource;
                        sqlDataSource.ConnectionString = connectionString;
                    }
                    foreach (var parameter in report.ReportParameters)
                    {
                        if (parameter.AvailableValues.DataSource is SqlDataSource)
                        {
                            var sqlDataSource = (SqlDataSource)parameter.AvailableValues.DataSource;
                            sqlDataSource.ConnectionString = connectionString;
                        }
                    }
                }
    
                foreach (var item in reportItemBase.Items)
                {
                    //recursively set the connection string to the items from the Items collection
                    SetConnectionString(item);
    
                    if (item is SubReport)
                    {
                        var subReport = (SubReport)item;
                        subReport.ReportSource = this.UpdateReportSource(subReport.ReportSource);
                        continue;
                    }
    
                    //Covers all data items(Crosstab, Table, List, Graph, Map and Chart)
                    if (item is DataItem)
                    {
                        var dataItem = (DataItem)item;
                        if (dataItem.DataSource is SqlDataSource)
                        {
                            var sqlDataSource = (SqlDataSource)dataItem.DataSource;
                            sqlDataSource.ConnectionString = connectionString;
                            continue;
                        }
                    }
                }
            }
        }
    }
    

    My angular front end is of no use to you but hopefully you should get enough from the commented links to help you. Btw - I have noticed that Kendo Reports creates spikes in CPU demand, it's a hungry beast. Recently I have been toying with the idea of moving the above code into a separate web app in order to abstract it away from the main application. Let me know how you get on. Oh, and my app.config file:

    <configuration>
        <configSections>
        </configSections>
        <connectionStrings>
            <add name="Nuagecare.Reports.Properties.Settings.Nuagecare" connectionString="Data Source=.;Initial Catalog=Nuagecare;Integrated Security=True"
                providerName="System.Data.SqlClient" />
        </connectionStrings>
        <appSettings>
            <add key="owin:AutomaticAppStartup" value="false"/>
        </appSettings>
        <runtime>
            <gcServer enabled="true"/>
        </runtime>
    </configuration>
    
  • User Avatar
    0
    Mitch created

    Many thanks for sharing some of your code @bobingham

    I can relate to "I'm getting too old for this game", I first started coding for a living in the mid 80s when Pascal was popular. Not the multitude of languages we have to learn today.

    I don't feel so bad asking for help as you found "that this was a difficult process of trial and error."

    I'll have another go at getting it going, and report back how I get on.

    Cheers.

  • User Avatar
    0
    BobIngham created

    Oooooh, you must be a real techie. I had to make do with Cobol.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi Guys,

    Can we close this issue :).

  • User Avatar
    0
    BobIngham created

    You can for me, I'm sure @mitch has sorted his problem so won't mind this being closed.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Thanks :)

  • User Avatar
    0
    Mitch created

    Just a quick update @bobingham

    I decided at the weekend to use Stimulsoft Reports instead of Telerik Reports.

    My previous project used both Telerik reports and Stimulsoft Reports. I found Stimulsoft reliable and easy to install as opposed to Telerik which was tricky to install and slightly fragile.

    I wanted to stick with Telerik for my latest project as I use Telerik ASP Net Core controls and there is a bit of an advantage to sticking with a single vendor.

    However, faffing about with Telerik installing reports in ANZ has put me off. I got Stimulsoft installed and working in about 20 minutes.

    Hopefully it's going to be the correct decision in the long run.

    Thanks again for your input Bob.