Skip to main content
A newer version of this page is available. .

ReportStorageWebExtension Class

A report storage for Web Reporting.

Namespace: DevExpress.XtraReports.Web.Extensions

Assembly: DevExpress.XtraReports.v22.1.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:

  1. IWebDocumentViewerReportResolver (Web Document Viewer only)
  2. IReportProvider
  3. 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:

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:

    Show code
    using DevExpress.XtraReports.UI;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.ServiceModel;
    
    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;
        }
    }
    
    public static class ReportsFactory
    {
        static ReportsFactory()
        {
            Reports.Add("TestReport", () => new TestReport());
        }
        public static Dictionary<string, Func<XtraReport>> Reports = new Dictionary<string, Func<XtraReport>>();
    }
    
  • Report Storage based on a SQLite database:

    Show code
    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;
            }
        }
    }
    
    using System.Linq;
    using Microsoft.EntityFrameworkCore;
    
    namespace AspNetCoreSample.Data {
        public class SqlDataConnectionDescription : DataConnection { }
        public class JsonDataConnectionDescription : DataConnection { }
        public abstract class DataConnection {
            public int Id { get; set; }
            public string Name { get; set; }
            public string DisplayName { get; set; }
            public string ConnectionString { get; set; }
        }
    
        public class ReportItem {
            public int Id { get; set; }
            public string Name { get; set; }
            public string DisplayName { get; set; }
            public byte[] LayoutData { get; set; }
        }
    
        public class ReportDbContext : DbContext {
            public DbSet<JsonDataConnectionDescription> JsonDataConnections { get; set; }
            public DbSet<SqlDataConnectionDescription> SqlDataConnections { get; set; }
            public DbSet<ReportItem> Reports { get; set; }
            public ReportDbContext(DbContextOptions<ReportDbContext> options) : base(options) {
            }
            public void InitializeDatabase() {
                Database.EnsureCreated();
    
                var nwindJsonDataConnectionName = "NWindProductsJson";
                if(!JsonDataConnections.Any(x => x.Name == nwindJsonDataConnectionName)) {
                    var newData = new JsonDataConnectionDescription {
                        Name = nwindJsonDataConnectionName,
                        DisplayName = "Northwind Products (JSON)",
                        ConnectionString = "Uri=Data/nwind.json"
                    };
                    JsonDataConnections.Add(newData);
                }
    
    
                var nwindSqlDataConnectionName = "NWindConnectionString";
                if(!SqlDataConnections.Any(x => x.Name == nwindSqlDataConnectionName)) {
                    var newData = new SqlDataConnectionDescription {
                        Name = nwindSqlDataConnectionName,
                        DisplayName = "Northwind Data Connection",
                        ConnectionString = "XpoProvider=SQLite;Data Source=|DataDirectory|/Data/nwind.db"
                    };
                    SqlDataConnections.Add(newData);
                }
    
                var reportsDataConnectionName = "ReportsDataSqlite";
                if(!SqlDataConnections.Any(x => x.Name == reportsDataConnectionName)) {
                    var newData = new SqlDataConnectionDescription {
                        Name = reportsDataConnectionName,
                        DisplayName = "Reports Data (Demo)",
                        ConnectionString = "XpoProvider=SQLite;Data Source=|DataDirectory|/Data/reportsData.db"
                    };
                    SqlDataConnections.Add(newData);
                }
                SaveChanges();
            }
        }
    }
    
    using DevExpress.XtraReports.UI;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace AspNetCoreSample.PredefinedReports
    {
        public static class ReportsFactory
        {
            public static Dictionary<string, Func<XtraReport>> Reports = new Dictionary<string, Func<XtraReport>>()
            {
                ["TestReport"] = () => new TestReport()
            };
        }
    }
    

Inheritance

Object
ReportStorageWebExtension
See Also