ReportStorageWebExtension Class
A report storage for Web Reporting.
Namespace: DevExpress.XtraReports.Web.Extensions
Assembly: DevExpress.XtraReports.v21.2.Web.dll
NuGet Package: DevExpress.Web.Reporting.Common
Declaration
public abstract class ReportStorageWebExtension :
IReportStorageTool,
IReportStorageWebTool,
IReportStorageTool2
Remarks
When you bind the Web Document Viewer or Report Designer to a report specified by name (a string identifier), the control uses custom services, registered in the application, to resolve a report name to a report object. The services are queried in the following order:
- IWebDocumentViewerReportResolver (Web Document Viewer only)
- IReportProvider
- ReportStorageWebExtension
When you save a report in the Web Report Designer, the ReportStorageWebExtension service is called to save the report data.
Create the ReportStorageWebExtension class descendant and register it in your application to implement a custom report storage.
Refer to the following help topics for more information:
- Add a Report Storage (ASP.NET Web Forms)
- Add a Report Storage (ASP.NET MVC)
- Add a Report Storage (ASP.NET Core)
- Create a Blazor Reporting (JavaScript-Based) Application
Examples
For a code example, create a new Web project from the DevExpress Visual Studio template and review the code for the CustomReportStorageWebExtension
class in the Services folder.
Report Storage based on a file system:
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.ServiceModel; using DevExpress.XtraReports.Web.Extensions; using DevExpress.XtraReports.UI; using MvcSample.PredefinedReports; namespace MvcSample.Services { public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension { readonly string reportDirectory; const string FileExtension = ".repx"; public CustomReportStorageWebExtension(string reportDirectory) { if (!Directory.Exists(reportDirectory)) { Directory.CreateDirectory(reportDirectory); } this.reportDirectory = reportDirectory; } private bool IsWithinReportsFolder(string url, string folder) { var rootDirectory = new DirectoryInfo(folder); var fileInfo = new FileInfo(Path.Combine(folder, url)); return fileInfo.Directory.FullName.ToLower().StartsWith(rootDirectory.FullName.ToLower()); } public override bool CanSetData(string url) { // Determines whether a report with the specified URL can be saved. // Add custom logic that returns **false** for reports that should be read-only. // Return **true** if no valdation is required. // This method is called only for valid URLs (if the **IsValidUrl** method returns **true**). return true; } public override bool IsValidUrl(string url) { // Determines whether the URL passed to the current report storage is valid. // Implement your own logic to prohibit URLs that contain spaces or other specific characters. // Return **true** if no validation is required. return Path.GetFileName(url) == url; } public override byte[] GetData(string url) { // Uses a specified URL to return report layout data stored within a report storage medium. // This method is called if the **IsValidUrl** method returns **true**. // You can use the **GetData** method to process report parameters sent from the client // if the parameters are included in the report URL's query string. try { if (Directory.EnumerateFiles(reportDirectory).Select(Path.GetFileNameWithoutExtension).Contains(url)) { return File.ReadAllBytes(Path.Combine(reportDirectory, url + FileExtension)); } if (ReportsFactory.Reports.ContainsKey(url)) { using (MemoryStream ms = new MemoryStream()) { ReportsFactory.Reports[url]().SaveLayoutToXml(ms); return ms.ToArray(); } } } catch (Exception) { throw new FaultException(new FaultReason("Could not get report data."), new FaultCode("Server"), "GetData"); } throw new FaultException(new FaultReason(string.Format("Could not find report '{0}'.", url)), new FaultCode("Server"), "GetData"); } public override Dictionary<string, string> GetUrls() { // Returns a dictionary that contains the report names (URLs) and display names. // The Report Designer uses this method to populate the Open Report and Save Report dialogs. return Directory.GetFiles(reportDirectory, "*" + FileExtension) .Select(Path.GetFileNameWithoutExtension) .Union(ReportsFactory.Reports.Select(x => x.Key)) .ToDictionary<string, string>(x => x); } public override void SetData(XtraReport report, string url) { // Saves the specified report to the report storage with the specified name // (saves existing reports only). if(!IsWithinReportsFolder(url, reportDirectory)) throw new FaultException(new FaultReason("Invalid report name."), new FaultCode("Server"), "GetData"); report.SaveLayoutToXml(Path.Combine(reportDirectory, url + FileExtension)); } public override string SetNewData(XtraReport report, string defaultUrl) { // Allows you to validate and correct the specified name (URL). // This method also allows you to return the resulting name (URL), // and to save your report to a storage. The method is called only for new reports. SetData(report, defaultUrl); return defaultUrl; } } }
Report Storage based on a SQLite database:
using System.Collections.Generic; using System.IO; using System.Linq; using DevExpress.XtraReports.UI; using AspNetCoreSample.PredefinedReports; using AspNetCoreSample.Data; namespace AspNetCoreSample.Services { public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension { protected ReportDbContext DbContext { get; set; } public CustomReportStorageWebExtension(ReportDbContext dbContext) { this.DbContext = dbContext; } public override bool CanSetData(string url) { // Determines whether a report with the specified URL can be saved. // Add custom logic that returns **false** for reports that should be read-only. // Return **true** if no valdation is required. // This method is called only for valid URLs (if the **IsValidUrl** method returns **true**). return true; } public override bool IsValidUrl(string url) { // Determines whether the URL passed to the current report storage is valid. // Implement your own logic to prohibit URLs that contain spaces or other specific characters. // Return **true** if no validation is required. return true; } public override byte[] GetData(string url) { // Uses a specified URL to return report layout data stored within a report storage medium. // This method is called if the **IsValidUrl** method returns **true**. // You can use the **GetData** method to process report parameters sent from the client // if the parameters are included in the report URL's query string. var reportData = DbContext.Reports.FirstOrDefault(x => x.Name == url); if(reportData != null) return reportData.LayoutData; if(ReportsFactory.Reports.ContainsKey(url)) { using var ms = new MemoryStream(); using XtraReport report = ReportsFactory.Reports[url](); report.SaveLayoutToXml(ms); return ms.ToArray(); } throw new DevExpress.XtraReports.Web.ClientControls.FaultException( string.Format("Could not find report '{0}'.", url)); } public override Dictionary<string, string> GetUrls() { // Returns a dictionary that contains the report names (URLs) and display names. // The Report Designer uses this method to populate the Open Report and Save Report dialogs. return DbContext.Reports .ToList() .Select(x => x.Name) .Union(ReportsFactory.Reports.Select(x => x.Key)) .ToDictionary<string, string>(x => x); } public override void SetData(XtraReport report, string url) { // Saves the specified report to the report storage with the specified name // (saves existing reports only). using var stream = new MemoryStream(); report.SaveLayoutToXml(stream); var reportData = DbContext.Reports.FirstOrDefault(x => x.Name == url); if(reportData == null) { DbContext.Reports.Add(new ReportItem { Name = url, LayoutData = stream.ToArray() }); } else { reportData.LayoutData = stream.ToArray(); } DbContext.SaveChanges(); } public override string SetNewData(XtraReport report, string defaultUrl) { // Allows you to validate and correct the specified name (URL). // This method also allows you to return the resulting name (URL), // and to save your report to a storage. The method is called only for new reports. SetData(report, defaultUrl); return defaultUrl; } } }