Microsoft Azure Integration Specifics
- 12 minutes to read
This document is an overview of the requirements for reporting applications in the Microsoft Azure environment.
Rendering Engine Selection
DevExpress Reporting uses the Skia-based drawing engine when an application in hosted on Azure with Linux-based environments. Make sure that you added the DevExpress.Drawing.Skia package to your application to use this engine.
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:
- Use Visual Studio Templates to Create an ASP.NET Core Application with a Document Viewer
- Use .NET CLI Template to Create an ASP.NET Core Reporting Application with Report Designer
- Use Visual Studio Templates to Create an ASP.NET Core Application with a Report Designer
- Use .NET CLI Template to Create an ASP.NET Core Reporting Application with Report Designer
ASP.NET MVC and Web Forms
Before you run web reporting controls on Azure, install the following packages:
- For Web Forms applications, install the DevExpress.Web.Reporting NuGet package.
Install the DevExpress.Web.Reporting.Azure NuGet package. The package contains services for web reporting applications in the Microsoft Azure environment. Review the following help topic for more information: Choose Between Offline and Online DevExpress NuGet Feeds.
Install the latest versions of the following NuGet packages. Use the NuGet Package Manager to automatically add binding redirects for corresponding assemblies.
For more information on the redirect technique, review the following document: Redirecting assembly versions.
Add a reference to the DevExpress.XtraReports.v24.2.Web.Azure.dll assembly to your project. Include the assembly in the deployment file list.
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:
- Azure Cloud Services or Web App (single instance)
- Azure Cloud Services or Web App (multiple instances with session affinity (ARR affinity) enabled)
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 older, and 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 Web Document Viewer internal services to services designed for a decentralized Azure environment. This 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 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 preventing 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, 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:
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; } // ... }
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.
Users can modify reports in the following ways:
- change report parameters
- perform drill-down
- perform interactive sorting.
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 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
PDF Rendering
If your Reporting application includes XRPdfContent controls, configure the application to use the Skia rendering engine.
Custom Fonts
Your report design and layout may rely on a font type that is not available in the application’s hosting environment. If a font cannot be installed on the client machine, in a Docker image, in an Azure, or in another host/container, your report replaces unavailable fonts with default fonts. This may alter the appearance of report pages from the original design.
The DevExpress Reporting suite helps you ensure that a report uses the correct fonts regardless of the hosting environment. DXFontRepository
is used as a font storage that manages a collection of available fonts for document creation. A report control raises the DXFontRepository.QueryNotFoundFont event that notifies you about missing typefaces so that you can obtain required font data. Once you obtain these fonts, add them to your report’s DXFontRepository, thus making them available to report controls.
Unlike the PrivateFontCollection, which is not supported in non-Windows environments, the DXFontRepository
is a cross-platform solution that helps you manage and access fonts in DevExpress Reporting.
Refer to the following article for more information: Use Custom Fonts in Reporting.
Image Rendering
The WinControlContainer control is rendered as bitmap despite its 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.