Skip to main content Skip to footer

Decouple Report Storage from the Reporting Back-End in ASP.NET Core

In a previous article, we described how to split a monolith reporting application into two parts (client-facing application and reporting service). One of the advantages of this approach is independent development and deployment. It eliminates the need to rebuild client-facing apps after adding or updating reports in the reporting service.

ActiveReports allows you decouple report storage from the service. You won’t need to need to rebuild it after updating or modifying reports, and the separation provides more freedom for report management. Use the same storage for an application that displays reports and a web-designer application.

The following tutorial explains how to set up the reporting services application so that it obtains reports from Azure Files.

Note: this tutorial is using the monolith application for the sake of simplicity, but the same code can be used with the separate reporting service application.

  • Create Azure Storage Account and file share or use the existing ones
  • Upload a page report into the share's root folder, note the report's name
  • In Visual Studio 2019, create a new "ActiveReports 14 JS Viewer Core MVC" Application
  • Install the following packages into the newly created project:

    1. Microsoft.Azure.Storage.Blob
    2. Microsoft.Azure.Storage.Common
    3. Microsoft.Azure.Storage.File
    4. GrapeCity.ActiveReports
  • Add appsettings.json file into a project

  • Replace the following:

    • myaccount with your storage account name
    • StorageAccountKeyEndingIn== with your storage account key
    • myshare with your share name
      {  
      "connectionString": "DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=StorageAccountKeyEndingIn==;EndpointSuffix=core.windows.net",  
      "share": "myshare"  
      }
      
  • Add AzureReportsStorage.cs file into the project
  • Copy and paste the following class declaration:
    • (This class encapsulates reports content obtained from the Azure file storage)
public class AzureReportsStorage  
{  
         private Microsoft.Azure.Storage.File.CloudFileShare _share;  
         public AzureReportsStorage(string connString, string shareName)  
         {  
                 var storageAccount = Microsoft.Azure.Storage.CloudStorageAccount.Parse(connString);  
                 // Create a CloudFileClient object for credentialed access to Azure Files.  
                 var fileClient = storageAccount.CreateCloudFileClient();  
                 // Get a reference to the file share we created previously.  
                 this._share = fileClient.GetShareReference(shareName);  
         }  
        public GrapeCity.ActiveReports.PageReportModel.Report GetReport(string fileName)  
        {  
            if (this._share.Exists())  
            {  
                // Get a reference to the root directory for the share.  
                Microsoft.Azure.Storage.File.CloudFileDirectory rootDir = _share.GetRootDirectoryReference();

                // Ensure that the directory exists.  
                if (rootDir.Exists())  
                {  
                    // Get a reference to the file we created previously.  
                    Microsoft.Azure.Storage.File.CloudFile file = rootDir.GetFileReference(fileName);

                    // Ensure that the file exists.  
                    if (file.Exists())  
                    {  
                        var ms = new System.IO.MemoryStream();  
                        file.DownloadToStream(ms);  
                        ms.Position = 0;  
                        using (var reader = new System.IO.StreamReader(ms))  
                        {  
                            var rep =  new GrapeCity.ActiveReports.PageReport(reader);  
                            return rep.Report;  
                        }  
                    }  
                    else  
                    {  
                        return null;  
                    }  
                }  
                else  
                {  
                    return null;  
                }

            }  
            else  
            {  
                return null;  
            }  
        }

}
  • Modify Startup constructor in Startup.cs file so it accepts IConfiguration instance and initializes the instance of AzureReportsStorage class passing connection string and share name that are read from appsettings.json:
private AzureReportsStorage _storage;  
public Startup(IConfiguration configuration)  
{  
        _storage = new AzureReportsStorage(configuration["connectionString"], configuration["share"]);  
}
  • Modify the code of Startup.Configure method so the reporting service obtains reports from the cloud storage
    • UseCustomStore method lets you set up any type of report storage. This code invokes AzureReportsStorage.getReport:
      app.UseReporting(settings =>  
      {  
          // settings.UseEmbeddedTemplates(EmbeddedReportsPrefix, Assembly.GetAssembly(GetType()));  
          settings.UseCustomStore((reportName) =>  
          {  
                 return this._storage.GetReport(reportName);  
          });  
          settings.UseCompression = true;  
      });
      
  • Open wwwroot\index.html file
  • Replace "Report1.rdlx" in the line viewer.openReport("RdlReport1.rdlx"); with the report name that was uploaded into the cloud file share in step two
  • Run the Project and make sure that report content is displayed properly

Find the complete application code in the attachment.

We hope decoupling storage improves your current project. Let us know how you’ve implemented it in your work.

Sergey Abakumoff

Sergey Abakumoff

Product Engineer
comments powered by Disqus