Skip to main content

Microsoft Azure Integration Specifics

  • 12 minutes to read

This document is an overview of the requirements for reporting applications in the Microsoft Azure environment.

Distributed Storage Specifics

The Document Viewer and Report Designer preview mode require access to the document during its generation. A report and document cache is used to organize temporary information storage between requests to avoid excessive document generation operations.

For more information on mechanisms of caching and document creation, review the following help topics:

ASP.NET Core

DevExpress Reporting integrated in Azure can use the following storage techniques:

  • S3 Compatible Storage and Distributed Cache implemented in ASP.NET Core apps
  • Azure Database connected through XPO
  • Azure Cloud Storage. To use Azure cloud storage, you must have the DevExpress.AspNetCore.Reporting.Azure NuGet package installed.

You can create sample apps that use different cache providers using our project templates. For more information, review the following help topics:

ASP.NET MVC and Web Forms

Before you run web reporting controls on Azure, install the following packages:

Hosting Specifics

Create Azure Cloud Storage

Create an Azure Storage account to persist report definitions and documents in Azure storage. As a result, you enable the Document Viewer to preserve its state after an application restarts.

Select the Standard general-purpose storage account. This account type includes the Blob Storage, Queue Storage, and Table Storage Azure services that are required by DevExpress Reporting components. For more information on storage account types, review the following article: Storage account overview.

Select the Azure Storage account replication (LRS, ZRS, GRS, RA-GRS) that is most appropriate for your deployment strategy. Exclude the Zone Redundant Storage (ZRS) option because it works with blob containers only, and the Web Document Viewer requires both tables and blobs.

Single Instance Hosting

Suppose that your application relies on one of the following hosting service configurations:

In this situation, you must use one of the alternative options described below to configure the application.

Option 1: Use Cached Report Source Builder

The Document Viewer component and the Report Designer component in Preview mode require access to the document while it is being generated.

The UseCachedReportSourceBuilder method allows the application to store document pages in a persistent storage (Azure Cloud Storage) while the document is generated. This is critical to prevent data loss when the Load Balancer routes a request to an application instance where the document is not generated or when an application pool is reloaded.

The following code snippets show how to configure the application to use the Cached Report Source Builder:

  • ASP.NET Core

    using DevExpress.AspNetCore.Reporting;
    using DevExpress.AspNetCore.Reporting.Azure;
    using DevExpress.XtraReports.Web.Azure;
    using DevExpress.XtraReports.Web.WebDocumentViewer;
    
    var builder = WebApplication.CreateBuilder(args);
    // ...
    builder.Services.ConfigureReportingServices(configurator => {
      configurator.ConfigureWebDocumentViewer(viewerConfigurator => {
          viewerConfigurator.UseCachedReportSourceBuilder();
          viewerConfigurator.UseAzureCachedReportSourceBuilder(builder.Configuration.GetConnectionString("AzureStorageConnectionString"), StorageSynchronizationMode.InterThread);
      });
    });
    // ...
    var app = builder.Build();
    using(var scope = app.Services.CreateScope()) {
      var azureResourceInitializer = scope.ServiceProvider.GetService<IAzureResourceProvisionService>();
      azureResourceInitializer.CreateRequiredResourcesAsync().GetAwaiter().GetResult();
    }
    
  • ASP.NET MVC and Web Forms

    using DevExpress.XtraReports.Web.Azure;
    using DevExpress.XtraReports.Web.Azure.WebDocumentViewer;
    // ...
    protected void Application_Start(object sender, System.EventArgs e) {
      //...  
      AzureWebDocumentViewerContainer.UseCachedReportSourceBuilder(cloudStorageConnectionString);
      ASPxWebDocumentViewer.StaticInitialize();
      ((IAzureResourceProvisionService)DefaultWebDocumentViewerContainer.Current.GetService(typeof(IAzureResourceProvisionService))).CreateRequiredResourcesAsync().GetAwaiter().GetResult();
    }
    

Option 2: Call the UseAzureEnvironment Method

This method is used in v.19.2 and earlier, and it is still applicable. It replaces the Document Viewer and Report Designer internal services with services adjusted for Azure.

The following code snippet calls the UseAzureEnvironment method at application startup:

  • ASP.NET Core

    using DevExpress.AspNetCore.Reporting;
    using DevExpress.AspNetCore.Reporting.Azure;
    using DevExpress.XtraReports.Web.Azure;
    using DevExpress.XtraReports.Web.WebDocumentViewer;
    
    var builder = WebApplication.CreateBuilder(args);
    // ...
    builder.Services.ConfigureReportingServices(configurator => {
      configurator.ConfigureWebDocumentViewer(viewerConfigurator => {
          viewerConfigurator.UseAzureEnvironment(cloudStorageConnectionString, serviceBusConnectionString);
      });
    });
    // ...
    var app = builder.Build();
    using(var scope = app.Services.CreateScope()) {
      var azureResourceInitializer = scope.ServiceProvider.GetService<IAzureResourceProvisionService>();
      azureResourceInitializer.CreateRequiredResourcesAsync().GetAwaiter().GetResult();
    }
    
  • ASP.NET MVC and Web Forms

    using DevExpress.XtraReports.Web.Azure;
    using DevExpress.XtraReports.Web.Azure.WebDocumentViewer;
    // ...
    protected void Application_Start(object sender, System.EventArgs e) {
      //...  
      AzureWebDocumentViewerContainer.UseAzureEnvironment(cloudStorageConnectionString);
      ASPxWebDocumentViewer.StaticInitialize();
      ((IAzureResourceProvisionService)DefaultWebDocumentViewerContainer.Current.GetService(typeof(IAzureResourceProvisionService))).CreateRequiredResourcesAsync().GetAwaiter().GetResult();
    }
    

This method switches the Web Document Viewer’s internal services to services designed for the decentralized Azure environment. The method also affects the Web Report Designer because the Designer’s preview uses the Web Document Viewer to display a document.

If you use the Report Designer in an ASP.NET MVC or ASP.NET Web Forms application, you can also call the AzureReportDesignerContainer.UseAzureEnvironment method at application startup and specify the Azure Storage connection string as the method’s parameter. The Web Report Designer encrypts the connection information sent to the client. The UseAzureEnvironment method instructs the Report Designer to store this information in Azure Table storage.

The following code calls the UseAzureEnvironment method for the application with the Report Designer:

  • ASP.NET MVC and Web Forms

    using DevExpress.XtraReports.Web.Azure;
    using DevExpress.XtraReports.Web.Azure.ReportDesigner;
    // ...
    
    protected void Application_Start(object sender, System.EventArgs e) {
      AzureReportDesignerContainer.UseAzureEnvironment(cloudStorageConnectionString);
      ASPxWebDocumentViewer.StaticInitialize();
      ((IAzureResourceProvisionService)DefaultWebDocumentViewerContainer.Current.GetService(typeof(IAzureResourceProvisionService))).CreateRequiredResourcesAsync().GetAwaiter().GetResult();
    }
    

Multi-instance Hosting

You can host the Web Document Viewer on multiple web application instances if you disable session affinity (ARR affinity).

To scale out a web application to multiple instances, use one of the alternative options described below:

Option 1: Use the Cached Report Source Builder

The Document Viewer component and the Report Designer component in Preview mode require access to a document while it is being generated.

The UseCachedReportSourceBuilder method allows the application to store document pages in a persistent storage (Azure Cloud Storage) while the document is generated. This is critical to prevent data loss when the Load Balancer routes a request to an application instance where the document is not generated or when an application pool is reloaded.

Web Document Viewer: Azure Cloud Storage Diagram

The following code snippets show how to configure the application to use the Cached Report Source Builder:

  • ASP.NET Core

    using DevExpress.AspNetCore.Reporting;
    using DevExpress.AspNetCore.Reporting.Azure;
    using DevExpress.XtraReports.Web.Azure;
    using DevExpress.XtraReports.Web.WebDocumentViewer;
    
    var builder = WebApplication.CreateBuilder(args);
    // ...
    builder.Services.ConfigureReportingServices(configurator => {
      configurator.ConfigureWebDocumentViewer(viewerConfigurator => {
          viewerConfigurator.UseCachedReportSourceBuilder();
          viewerConfigurator.UseAzureCachedReportSourceBuilder(builder.Configuration.GetConnectionString("AzureStorageConnectionString"), StorageSynchronizationMode.InterThread);
      });
    });
    // ...
    var app = builder.Build();
    using(var scope = app.Services.CreateScope()) {
      var azureResourceInitializer = scope.ServiceProvider.GetService<IAzureResourceProvisionService>();
      azureResourceInitializer.CreateRequiredResourcesAsync().GetAwaiter().GetResult();
    }
    
  • ASP.NET MVC and Web Forms

    using DevExpress.XtraReports.Web.Azure;
    using DevExpress.XtraReports.Web.Azure.WebDocumentViewer;
    // ...
    protected void Application_Start(object sender, System.EventArgs e) {
      //...  
      AzureWebDocumentViewerContainer.UseCachedReportSourceBuilder(cloudStorageConnectionString, StorageSynchronizationMode.InterProcess);
      ASPxWebDocumentViewer.StaticInitialize();
      ((IAzureResourceProvisionService)DefaultWebDocumentViewerContainer.Current.GetService(typeof(IAzureResourceProvisionService))).CreateRequiredResourcesAsync().GetAwaiter().GetResult();
    }
    

The StorageSynchronizationMode.InterProcess parameter enables inter-process synchronization.

Option 2: Use Service Bus

Set up the Viewer for a Service Bus. A Service Bus tier should be either Standard or Premium (since both Sessions and Topics are required).

The Service Bus requires the Azure.Messaging.ServiceBus 17.17.4+ NuGet package. The Service Bus package is installed automatically when you install the DevExpress Azure NuGet package.

To use a service bus, call the UseAzureEnvironment method with parameters that specify the Azure Storage connection string and a connection string to access the service bus at application startup:

  • ASP.NET Core

    using DevExpress.AspNetCore.Reporting;
    using DevExpress.AspNetCore.Reporting.Azure;
    using DevExpress.XtraReports.Web.Azure;
    using DevExpress.XtraReports.Web.WebDocumentViewer;
    
    var builder = WebApplication.CreateBuilder(args);
    // ...
    builder.Services.ConfigureReportingServices(configurator => {
      configurator.ConfigureWebDocumentViewer(viewerConfigurator => {
          viewerConfigurator.UseAzureEnvironment(cloudStorageConnectionString, serviceBusConnectionString);
      });
    });
    // ...
    var app = builder.Build();
    using(var scope = app.Services.CreateScope()) {
      var azureResourceInitializer = scope.ServiceProvider.GetService<IAzureResourceProvisionService>();
      azureResourceInitializer.CreateRequiredResourcesAsync().GetAwaiter().GetResult();
    }
    
  • ASP.NET Web Forms and MVC

    using DevExpress.XtraReports.Web;
    using DevExpress.XtraReports.Web.Azure;
    using DevExpress.XtraReports.Web.Azure.WebDocumentViewer;
    // ...
    
    protected void Application_Start(object sender, System.EventArgs e) {
      AzureWebDocumentViewerContainer.UseAzureEnvironment(cloudStorageConnectionString, serviceBusConnectionString);
      ASPxWebDocumentViewer.StaticInitialize();
      ((IAzureResourceProvisionService)DefaultWebDocumentViewerContainer.Current.GetService(typeof(IAzureResourceProvisionService))).CreateRequiredResourcesAsync().GetAwaiter().GetResult();
    }
    

    Make sure that you call the ASPxWebDocumentViewer.StaticInitialize method after all other methods.

Option 3: Use Synchronous Document Creation

If a report document is generated asynchronously, and HttpContext.Session is not available in the report’s event handlers, force synchronous document creation as follows:

  1. Create a custom WebDocumentViewerOperationLogger implementation that overrides the BuildStarting method. This method’s code should call the XtraReport.CreateDocument method, as illustrated below:

    using DevExpress.XtraReports.UI;
    using DevExpress.XtraReports.Web.ReportDesigner;
    using DevExpress.XtraReports.Web.WebDocumentViewer;
    
    public class CustomLogger : WebDocumentViewerOperationLogger {
        public override Action BuildStarting(string reportId, string reportUrl, XtraReport report, ReportBuildProperties buildProperties) {   
        report.CreateDocument();
        return null;
        }
        // ...
    }
    
  2. At application startup, call the UseAzureEnvironment method with the parameter that specifies the Azure Storage connection string and register the CustomLogger class implemented in the previous step.

    • ASP.NET Core

      using DevExpress.AspNetCore.Reporting;
      using DevExpress.AspNetCore.Reporting.Azure;
      using DevExpress.XtraReports.Web.Azure;
      using DevExpress.XtraReports.Web.WebDocumentViewer;
      
      var builder = WebApplication.CreateBuilder(args);
      // ...
      builder.Services.ConfigureReportingServices(configurator => {
      configurator.ConfigureWebDocumentViewer(viewerConfigurator =>   {
          viewerConfigurator.UseAzureEnvironment(cloudStorageConnectionString);
        });
      });
      builder.Services.AddTransient<WebDocumentViewerOperationLogger, CustomLogger>();
      // ...
      var app = builder.Build();
      using(var scope = app.Services.CreateScope()) {
        var azureResourceInitializer = scope.ServiceProvider.GetService<IAzureResourceProvisionService>();
        azureResourceInitializer.CreateRequiredResourcesAsync().GetAwaiter().GetResult();
      }
      
    • ASP.NET Web Forms and MVC

      using DevExpress.XtraReports.Web;
      using DevExpress.XtraReports.Web.Azure;
      using DevExpress.XtraReports.Web.Azure.WebDocumentViewer;
      // ...
      
      protected void Application_Start(object sender, System.EventArgs e) {
        AzureWebDocumentViewerContainer.UseAzureEnvironment(cloudStorageConnectionString);
        DefaultWebDocumentViewerContainer.RegisterSingleton<WebDocumentViewerOperationLogger, CustomLogger>();
        ASPxWebDocumentViewer.StaticInitialize();
        ((IAzureResourceProvisionService)DefaultWebDocumentViewerContainer.Current.GetService(typeof(IAzureResourceProvisionService))).CreateRequiredResourcesAsync().GetAwaiter().GetResult();
      }
      

      Make sure that you call the ASPxWebDocumentViewer.StaticInitialize method after all other methods.

The user can modify the report in the following ways:

In this scenario, do not pass the report instance to the Viewer’s OpenReport method. This is because the report instance may not be available on other machines that handle corresponding HTTP requests. Instead, use another OpenReport overload that takes the unique report name as a parameter. To translate a report name to a report instance, an application requires a report name resolution service. The IReportProvider interface can be utilized to implement this service. For more information, review the following help topic: Open a Report in ASP.NET Core Application.

Report Serialization

Web Document Viewer in an Azure environment serializes report definitions to XML. The REPX file that is generated is saved in Blob Storage.

A request that submits report parameter values or restores a drill-drown/drill-through/interactive sorting state requires a report instance in the following cases:

  • If the Document Viewer’s request is routed to another server instance (when multiple server instances are used).
  • When the Document Viewer’s request is routed to a single server instance, but the application pool is shut down due to inactivity.

A report instance is restored from an XML file and contains only settings that have been serialized (other settings are lost). You can use a custom WebDocumentViewerOperationLogger implementation to modify a report and apply settings.

Implement Custom Storages

Microsoft Azure is a distributed system, and virtual machines are shared between different clients. Information stored on a virtual machine can be lost when a Load Balancer turns this machine off and switches to a new machine.

To prevent information loss, the Web Document Viewer stores service information in Table Storage and saves document files in Blob Storage.

You can use a custom storage instead of Table and Blob storages. To do this, implement the following interface and substitute related services in a container:

Note

Web Document Viewer creates and uses “dxxrreports“ and “dxxrdocuments“ tables in Table Storage, and the “dxxrcommonstorage“ blob container in Blob Storage.

Tips and Limitations

XRRichText Control

If your report contains the XRRichText control, set the AzureCompatibility.Enable property to true at application startup. This setting is required to ensure the control renders correctly when the report is previewed, exported, or printed.

PDF Fonts

The PDF export engine used in DevExpress Reporting relies on system GDI calls; several of these calls are not supported in Azure.

When a web app is hosted under the Free or Shared plan, the following limitations apply:

  • right-to-left languages do not render correctly;
  • only non-embedded fonts are supported.

PDF Rendering

If your Reporting application includes XRPdfContent controls, configure the application to use the Skia rendering engine.

Custom Fonts

Image Rendering

All controls that can be rendered as metafiles or bitmaps are rendered as bitmaps. The XRChart, XRSparkline, XRGauge, and WinControlContainer controls are rendered as bitmaps despite their ImageType property value.

Caching Options

Azure Caching Settings

Applications that target ASP.NET Web Forms and MVC frameworks have the following default ‘Time to Live’ settings:

  • Report: 2 minutes.
  • Document: 1 minute.

Use the CacheCleanerSettings class to adjust cache settings.

Document Viewer Lifecycle

For information about the mechanisms implemented in the Document Viewer, including the client-sever communication protocol and server-side report storage, review the following help topic: Document Viewer Lifecycle.

See Also