Example T915105
Visible to All Users

Reporting for Web - How to Use the Object Data Source in Web Reporting Applications

This example includes simple projects with report components (Document Viewer and Report Designer) for ASP.NET WebForms, ASP.NET MVC, ASP.NET Core, and Blazor Server (JavaScript-based) platforms. The project demonstrates how to create an object data source, bind it to a report, and restore the object data source when Document Viewer or Report Designer loads a report from a REPX file.

The ObjectDataSource serves as an intermediate layer between the report and the collection of data elements to which the report is bound. The advantage of ObjectDataSource is that it is serialized in the report definition file (REPX). Otherwise, if you bind the report to a collection of custom objects, the data source information cannot be serialized, and you cannot restore the data source when you load the report from the REPX file.

Assign an ObjectDataSource instance to the DataSource property of the report. The ObjectDataSource instance specifies the type, constructors, methods, and properties of a custom object that creates a collection of data items. The custom object can contain a method that populates the collection of data items at runtime or retrieves data from any external data source.

The CustomWebDocumentViewerReportResolver service in this example implements the IWebDocumentViewerReportResolver interface. The Document Viewer calls that before loading a report specified by name. The service creates a report, assigns the data source, and passes the report to the Document Viewer.

The CustomReportStorageWebExtension service loads and saves a report specified by name in the Report Designer. The service is a ReportStorageWebExtension descendant.

The CustomObjectDataSourceConstructorFilterService implements the IObjectDataSourceConstructorFilterService interface and filters the list of constructors available for the ObjectDataSource in the Report Wizard and Data Source Wizard.

The ObjectDataSourceWizardCustomTypeProvider service implements the IObjectDataSourceWizardTypeProvider interface and defines the list of data types displayed for the ObjectDataSource in the Report Wizard and Data Source Wizard.

This example also demonstrates how to map a report parameter to the parameter used to filter data retrieved from the data source.

Files to Review

ASP.NET WebForms:

ASP.NET MVC:

ASP.NET Core:

Blazor:

Documentation

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)

Example Code

WebForms/CS/Reporting_ObjectDS_WebForms/ObjectDataSource/EmployeesDataSource.cs
C#
using System.Collections.Generic; using System.ComponentModel; using System.Linq; namespace Reporting_ObjectDS_WebForms.Employees { [DisplayName("Employees")] public class EmployeeList { public EmployeeList() { Items = InitializeList(); } public EmployeeList(int noOfItems) { Items = InitializeList().GetRange(1, noOfItems); } public List<DataItem> Items { get; set; } public List<DataItem> InitializeList() { return new List<DataItem>() { new DataItem(1, 101, "Andrew Fuller", "Dr.", "Vice President, Sales"), new DataItem(1, 102, "Anne Dodsworth", "Ms.", "Sales Representative"), new DataItem(1, 103, "Michael Suyama", "Mr.", "Sales Representative"), new DataItem(1, 104, "Janet Leverling", "Ms.", "Sales Representative"), new DataItem(1, 105, "Elliot Komaroff", "Dr.", "Sales Coordinator"), new DataItem(2, 201, "Nancy Davolio", "Ms.", "Sales Representative"), new DataItem(2, 202, "Steven Buchanan", "Mr.", "Sales Manager"), new DataItem(2, 203, "Laura Callahan", "Ms.", "Sales Coordinator"), new DataItem(3, 301, "Frédérique Citeaux", "Mr.", "Sales Coordinator"), new DataItem(3, 302, "Laurence Lebihan", "Mr.", "Sales Representative"), new DataItem(3, 303, "Elizabeth Lincoln", "Ms.", "Sales Manager"), new DataItem(3, 304, "Yang Wang", "Mr.", "Sales Representative"), new DataItem(4, 401, "Antonio Moreno", "Mr.", "Sales Representative"), new DataItem(4, 402, "Thomas Hardy", "Mr.", "Sales Representative"), new DataItem(4, 403, "Christina Berglund", "Ms.", "Sales Manager"), new DataItem(5, 501, "Alejandra Camino", "Ms.", "Sales Representative"), new DataItem(5, 502, "Matti Karttunen", "Mr.", "Sales Representative"), new DataItem(5, 503, "Rita Müller", "Mrs.", "Sales Representative"), }; } public List<DataItem> GetData(int noOfItems) { List<DataItem> revertList = new List<DataItem>(Items); revertList.Reverse(); return revertList.Take(noOfItems).ToList(); } } public class DataItem { public DataItem(int floor, int office, string personName, string titleOfCourtesy, string title) { Floor = floor; Office = office; PersonName = personName; TitleOfCourtesy = titleOfCourtesy; Title = title; } public int Floor { get; set; } public int Office { get; set; } public string PersonName { get; set; } public string TitleOfCourtesy { get; set; } public string Title { get; set; } } }
WebForms/VB/Reporting_ObjectDS_WebForms/ObjectDataSource/EmployeesDataSource.vb
Visual Basic
Imports System.Collections.Generic Imports System.ComponentModel Imports System.Linq Namespace Reporting_ObjectDS_WebForms.Employees <DisplayName("Employees")> Public Class EmployeeList Public Sub New() Items = InitializeList() End Sub Public Sub New(ByVal noOfItems As Integer) Items = InitializeList().GetRange(1, noOfItems) End Sub Public Property Items() As List(Of DataItem) Public Function InitializeList() As List(Of DataItem) Return New List(Of DataItem)() From { New DataItem(1, 101, "Andrew Fuller", "Dr.", "Vice President, Sales"), New DataItem(1, 102, "Anne Dodsworth", "Ms.", "Sales Representative"), New DataItem(1, 103, "Michael Suyama", "Mr.", "Sales Representative"), New DataItem(1, 104, "Janet Leverling", "Ms.", "Sales Representative"), New DataItem(1, 105, "Elliot Komaroff", "Dr.", "Sales Coordinator"), New DataItem(2, 201, "Nancy Davolio", "Ms.", "Sales Representative"), New DataItem(2, 202, "Steven Buchanan", "Mr.", "Sales Manager"), New DataItem(2, 203, "Laura Callahan", "Ms.", "Sales Coordinator"), New DataItem(3, 301, "Frédérique Citeaux", "Mr.", "Sales Coordinator"), New DataItem(3, 302, "Laurence Lebihan", "Mr.", "Sales Representative"), New DataItem(3, 303, "Elizabeth Lincoln", "Ms.", "Sales Manager"), New DataItem(3, 304, "Yang Wang", "Mr.", "Sales Representative"), New DataItem(4, 401, "Antonio Moreno", "Mr.", "Sales Representative"), New DataItem(4, 402, "Thomas Hardy", "Mr.", "Sales Representative"), New DataItem(4, 403, "Christina Berglund", "Ms.", "Sales Manager"), New DataItem(5, 501, "Alejandra Camino", "Ms.", "Sales Representative"), New DataItem(5, 502, "Matti Karttunen", "Mr.", "Sales Representative"), New DataItem(5, 503, "Rita Müller", "Mrs.", "Sales Representative") } End Function Public Function GetData(ByVal noOfItems As Integer) As List(Of DataItem) Dim revertList As New List(Of DataItem)(Items) revertList.Reverse() Return revertList.Take(noOfItems).ToList() End Function End Class Public Class DataItem Public Sub New(ByVal floor As Integer, ByVal office As Integer, ByVal personName As String, ByVal titleOfCourtesy As String, ByVal title As String) Me.Floor = floor Me.Office = office Me.PersonName = personName Me.TitleOfCourtesy = titleOfCourtesy Me.Title = title End Sub Public Property Floor() As Integer Public Property Office() As Integer Public Property PersonName() As String Public Property TitleOfCourtesy() As String Public Property Title() As String End Class End Namespace
WebForms/CS/Reporting_ObjectDS_WebForms/Services/CustomWebDocumentViewerReportResolver.cs
C#
using DevExpress.DataAccess.ObjectBinding; using DevExpress.XtraReports.UI; using DevExpress.XtraReports.Web.WebDocumentViewer; namespace Reporting_ObjectDS_WebForms { public class CustomWebDocumentViewerReportResolver : IWebDocumentViewerReportResolver { public XtraReport Resolve(string reportEntry) { if (reportEntry.StartsWith("EmployeeReport")) { XtraReport rep = CreateReport(reportEntry); rep.DataSource = CreateObjectDataSource(reportEntry); return rep; } return new XtraReport(); } private object CreateObjectDataSource(string reportName) { if (reportName == "EmployeeReport") { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(Employees.EmployeeList); dataSource.Constructor = ObjectConstructorInfo.Default; dataSource.DataMember = "Items"; return dataSource; } else if (reportName.EndsWith("7")) { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(Employees.EmployeeList); // Specify the parameter's default value. var parameter = new Parameter("noOfItems", typeof(int), 7); dataSource.Constructor = new ObjectConstructorInfo(parameter); dataSource.DataMember = "Items"; return dataSource; } else if (reportName.EndsWith("Parameter")) { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(Employees.EmployeeList); // Map data source parameter to report's parameter. var parameter = new Parameter() { Name = "noOfItems", Type = typeof(DevExpress.DataAccess.Expression), Value = new DevExpress.DataAccess.Expression("?parameterNoOfItems", typeof(int)) }; dataSource.Constructor = new ObjectConstructorInfo(parameter); dataSource.DataMember = "Items"; return dataSource; } else { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(Employees.EmployeeList); var parameterNoOfItems = new Parameter("noOfItems", typeof(int), 12); dataSource.Parameters.Add(parameterNoOfItems); dataSource.Constructor = ObjectConstructorInfo.Default; dataSource.DataMember = "GetData"; return dataSource; } } private XtraReport CreateReport(string reportEntry) { if (reportEntry.Contains("Parameter")) { XtraReport report = new PredefinedReports.EmployeeReport(); DevExpress.XtraReports.Parameters.Parameter param = new DevExpress.XtraReports.Parameters.Parameter() { Name = "parameterNoOfItems", Type = typeof(int), Value = 5 }; param.Description = "Number of Items"; report.Parameters.Add(param); report.RequestParameters = false; return report; } else return new PredefinedReports.EmployeeReport(); } } }
WebForms/VB/Reporting_ObjectDS_WebForms/Services/CustomWebDocumentViewerReportResolver.vb
Visual Basic
Option Infer On Imports DevExpress.DataAccess.ObjectBinding Imports DevExpress.XtraReports.UI Imports DevExpress.XtraReports.Web.WebDocumentViewer Namespace Reporting_ObjectDS_WebForms Public Class CustomWebDocumentViewerReportResolver Implements IWebDocumentViewerReportResolver Public Function Resolve(ByVal reportEntry As String) As XtraReport Implements IWebDocumentViewerReportResolver.Resolve If reportEntry.StartsWith("EmployeeReport") Then Dim rep As XtraReport = CreateReport(reportEntry) rep.DataSource = CreateObjectDataSource(reportEntry) Return rep End If Return New XtraReport() End Function Private Function CreateObjectDataSource(ByVal reportName As String) As Object If reportName = "EmployeeReport" Then #Region "ODS_Constructor" Dim dataSource As New ObjectDataSource() dataSource.Name = "EmployeeObjectDS" dataSource.DataSource = GetType(Employees.EmployeeList) dataSource.Constructor = ObjectConstructorInfo.Default dataSource.DataMember = "Items" Return dataSource #End Region Else If reportName.EndsWith("7") Then #Region "ODS_ConstructorWithParameter" Dim dataSource As New ObjectDataSource() dataSource.Name = "EmployeeObjectDS" dataSource.DataSource = GetType(Employees.EmployeeList) ' Specify the parameter's default value. Dim parameter = New Parameter("noOfItems", GetType(Integer), 7) dataSource.Constructor = New ObjectConstructorInfo(parameter) dataSource.DataMember = "Items" Return dataSource #End Region Else If reportName.EndsWith("Parameter") Then #Region "ODS_MapReportParameter" Dim dataSource As New ObjectDataSource() dataSource.Name = "EmployeeObjectDS" dataSource.DataSource = GetType(Employees.EmployeeList) ' Map data source parameter to report's parameter. Dim parameter = New Parameter() With {.Name = "noOfItems", .Type = GetType(DevExpress.DataAccess.Expression), .Value = New DevExpress.DataAccess.Expression("?parameterNoOfItems", GetType(Integer))} dataSource.Constructor = New ObjectConstructorInfo(parameter) dataSource.DataMember = "Items" Return dataSource #End Region Else #Region "ODS_Method" Dim dataSource As New ObjectDataSource() dataSource.Name = "EmployeeObjectDS" dataSource.DataSource = GetType(Employees.EmployeeList) Dim parameterNoOfItems = New Parameter("noOfItems", GetType(Integer), 12) dataSource.Parameters.Add(parameterNoOfItems) dataSource.DataMember = "GetData" dataSource.Constructor = ObjectConstructorInfo.Default Return dataSource #End Region End If End If End If End Function Private Function CreateReport(ByVal reportEntry As String) As XtraReport If reportEntry.Contains("Parameter") Then Dim report As XtraReport = New PredefinedReports.EmployeeReport() Dim param As New DevExpress.XtraReports.Parameters.Parameter() With {.Name = "parameterNoOfItems", .Type = GetType(Integer), .Value = 5} param.Description = "Number of Items" report.Parameters.Add(param) report.RequestParameters = False Return report Else Return New PredefinedReports.EmployeeReport() End If End Function End Class End Namespace
WebForms/CS/Reporting_ObjectDS_WebForms/Services/CustomReportStorageWebExtension.cs
C#
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.ServiceModel; using DevExpress.XtraReports.UI; using Reporting_ObjectDS_WebForms.PredefinedReports; 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; } public override bool CanSetData(string url) { // Determines whether or not it is possible to store a report by a given URL. // For instance, make the CanSetData method return false for reports that should be read-only in your storage. // This method is called only for valid URLs (i.e., if the IsValidUrl method returned true) before the SetData method is called. return true; } public override bool IsValidUrl(string url) { // Determines whether or not the URL passed to the current Report Storage is valid. // For instance, implement your own logic to prohibit URLs that contain white spaces or some other special characters. // This method is called before the CanSetData and GetData methods. return Path.GetFileName(url) == url; } public override byte[] GetData(string url) { // Returns report layout data stored in a Report Storage using the specified URL. // This method is called only for valid URLs after the IsValidUrl method is called. 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 of the existing report URLs and display names. // This method is called when running the Report Designer, // before the Open Report and Save Report dialogs are shown and after a new report is saved to a storage. 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) { // Stores the specified report to a Report Storage using the specified URL. // This method is called only after the IsValidUrl and CanSetData methods are called. var resolvedUrl = Path.GetFullPath(Path.Combine(reportDirectory, url + FileExtension)); if (!resolvedUrl.StartsWith(reportDirectory + Path.DirectorySeparatorChar)) { throw new FaultException("Invalid report name."); } report.SaveLayoutToXml(resolvedUrl); } public override string SetNewData(XtraReport report, string defaultUrl) { // Stores the specified report using a new URL. // The IsValidUrl and CanSetData methods are never called before this method. // You can validate and correct the specified URL directly in the SetNewData method implementation // and return the resulting URL used to save a report in your storage. SetData(report, defaultUrl); return defaultUrl; } }
WebForms/VB/Reporting_ObjectDS_WebForms/Services/CustomReportStorageWebExtension.vb
Visual Basic
Imports System Imports System.Collections.Concurrent Imports System.Collections.Generic Imports System.IO Imports System.Linq Imports System.Reflection Imports System.ServiceModel Imports DevExpress.XtraReports.Web.Extensions Imports DevExpress.XtraReports.UI Imports Reporting_ObjectDS_WebForms.PredefinedReports Namespace Reporting_ObjectDS_WebForms.Services Public Class CustomReportStorageWebExtension Inherits DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension Private ReadOnly reportDirectory As String Private Const FileExtension As String = ".repx" Public Sub New(ByVal reportDirectory As String) If Not Directory.Exists(reportDirectory) Then Directory.CreateDirectory(reportDirectory) End If Me.reportDirectory = reportDirectory End Sub Public Overrides Function CanSetData(ByVal url As String) As Boolean ' Determines whether or not it is possible to store a report by a given URL. ' For instance, make the CanSetData method return false for reports that should be read-only in your storage. ' This method is called only for valid URLs (i.e., if the IsValidUrl method returned true) before the SetData method is called. Return True End Function Public Overrides Function IsValidUrl(ByVal url As String) As Boolean ' Determines whether or not the URL passed to the current Report Storage is valid. ' For instance, implement your own logic to prohibit URLs that contain white spaces or some other special characters. ' This method is called before the CanSetData and GetData methods. Return Path.GetFileName(url) = url End Function Public Overrides Function GetData(ByVal url As String) As Byte() ' Returns report layout data stored in a Report Storage using the specified URL. ' This method is called only for valid URLs after the IsValidUrl method is called. Try If Directory.EnumerateFiles(reportDirectory).Select(AddressOf Path.GetFileNameWithoutExtension).Contains(url) Then Return File.ReadAllBytes(Path.Combine(reportDirectory, url & FileExtension)) End If If ReportsFactory.Reports.ContainsKey(url) Then Using ms As New MemoryStream() ReportsFactory.Reports(url)().SaveLayoutToXml(ms) Return ms.ToArray() End Using End If Catch e1 As Exception Throw New FaultException(New FaultReason("Could not get report data."), New FaultCode("Server"), "GetData") End Try Throw New FaultException(New FaultReason(String.Format("Could not find report '{0}'.", url)), New FaultCode("Server"), "GetData") End Function Public Overrides Function GetUrls() As Dictionary(Of String, String) ' Returns a dictionary of the existing report URLs and display names. ' This method is called when running the Report Designer, ' before the Open Report and Save Report dialogs are shown and after a new report is saved to a storage. Return Directory.GetFiles(reportDirectory, "*" & FileExtension).Select(AddressOf Path.GetFileNameWithoutExtension).Union(ReportsFactory.Reports.Select(Function(x) x.Key)).ToDictionary(Function(x) x) End Function Public Overrides Sub SetData(ByVal report As XtraReport, ByVal url As String) ' Stores the specified report to a Report Storage using the specified URL. ' This method is called only after the IsValidUrl and CanSetData methods are called. Dim resolvedUrl = Path.GetFullPath(Path.Combine(reportDirectory, url + FileExtension)) If Not resolvedUrl.StartsWith(reportDirectory + Path.DirectorySeparatorChar) Then Throw New FaultException("Invalid report name.") End If report.SaveLayoutToXml(resolvedUrl) End Sub Public Overrides Function SetNewData(ByVal report As XtraReport, ByVal defaultUrl As String) As String ' Stores the specified report using a new URL. ' The IsValidUrl and CanSetData methods are never called before this method. ' You can validate and correct the specified URL directly in the SetNewData method implementation ' and return the resulting URL used to save a report in your storage. SetData(report, defaultUrl) Return defaultUrl End Function End Class End Namespace
WebForms/CS/Reporting_ObjectDS_WebForms/Services/CustomObjectDataSourceConstructorFilterService.cs
C#
using DevExpress.DataAccess.Web; using Reporting_ObjectDS_WebForms.Employees; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; public class CustomObjectDataSourceConstructorFilterService : IObjectDataSourceConstructorFilterService { public IEnumerable<ConstructorInfo> Filter(Type dataSourceType, IEnumerable<ConstructorInfo> constructors) { if (dataSourceType == typeof(EmployeeList)) return constructors; else return constructors.Where(x => x.GetParameters().Length > 0); } }
WebForms/VB/Reporting_ObjectDS_WebForms/Services/CustomObjectDataSourceConstructorFilterService.vb
Visual Basic
Imports DevExpress.DataAccess.Web Imports Reporting_ObjectDS_WebForms.Employees Imports System Imports System.Collections.Generic Imports System.Linq Imports System.Reflection Public Class CustomObjectDataSourceConstructorFilterService Implements IObjectDataSourceConstructorFilterService Public Function Filter(ByVal dataSourceType As Type, ByVal constructors As IEnumerable(Of ConstructorInfo)) As IEnumerable(Of ConstructorInfo) Implements IObjectDataSourceConstructorFilterService.Filter If dataSourceType Is GetType(EmployeeList) Then Return constructors Else Return constructors.Where(Function(x) x.GetParameters().Length > 0) End If End Function End Class
WebForms/CS/Reporting_ObjectDS_WebForms/Services/ObjectDataSourceWizardCustomTypeProvider.cs
C#
using DevExpress.DataAccess.Web; using System; using System.Collections.Generic; namespace Reporting_ObjectDS_WebForms.Services { public class ObjectDataSourceWizardCustomTypeProvider : IObjectDataSourceWizardTypeProvider { public IEnumerable<Type> GetAvailableTypes(string context) { return new[] { typeof(Employees.EmployeeList) }; } } }
WebForms/VB/Reporting_ObjectDS_WebForms/Services/ObjectDataSourceWizardCustomTypeProvider.vb
Visual Basic
Imports DevExpress.DataAccess.Web Imports System Imports System.Collections.Generic Namespace Reporting_ObjectDS_WebForms.Services Public Class ObjectDataSourceWizardCustomTypeProvider Implements IObjectDataSourceWizardTypeProvider Public Function GetAvailableTypes(ByVal context As String) As IEnumerable(Of Type) Implements IObjectDataSourceWizardTypeProvider.GetAvailableTypes Return {GetType(Employees.EmployeeList)} End Function End Class End Namespace
WebForms/CS/Reporting_ObjectDS_WebForms/Global.asax.cs
C#
using System; using System.IO; using System.Web; using DevExpress.XtraReports.Web; using Reporting_ObjectDS_WebForms.Employees; using Reporting_ObjectDS_WebForms.Services; namespace Reporting_ObjectDS_WebForms { public class Global_asax : System.Web.HttpApplication { void Application_Start(object sender, EventArgs e) { System.Web.Routing.RouteTable.Routes.MapPageRoute("defaultRoute", "", "~/Default.aspx"); DevExpress.Utils.DeserializationSettings.RegisterTrustedClass(typeof(EmployeeList)); DevExpress.XtraReports.Configuration.Settings.Default.UserDesignerOptions.DataBindingMode = DevExpress.XtraReports.UI.DataBindingMode.Expressions; DevExpress.XtraReports.Web.WebDocumentViewer.Native.WebDocumentViewerBootstrapper.SessionState = System.Web.SessionState.SessionStateBehavior.Default; DevExpress.XtraReports.Web.QueryBuilder.Native.QueryBuilderBootstrapper.SessionState = System.Web.SessionState.SessionStateBehavior.Default; DevExpress.XtraReports.Web.ReportDesigner.Native.ReportDesignerBootstrapper.SessionState = System.Web.SessionState.SessionStateBehavior.Default; DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension.RegisterExtensionGlobal(new CustomReportStorageWebExtension(Server.MapPath("/Reports"))); DevExpress.XtraReports.Web.ReportDesigner.DefaultReportDesignerContainer.RegisterObjectDataSourceConstructorFilterService<CustomObjectDataSourceConstructorFilterService>(); DevExpress.XtraReports.Web.WebDocumentViewer.DefaultWebDocumentViewerContainer.Register<DevExpress.XtraReports.Web.WebDocumentViewer.IWebDocumentViewerReportResolver, CustomWebDocumentViewerReportResolver>(); DevExpress.XtraReports.Web.ReportDesigner.DefaultReportDesignerContainer.RegisterObjectDataSourceWizardTypeProvider<ObjectDataSourceWizardCustomTypeProvider>(); System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12; ASPxReportDesigner.StaticInitialize(); DevExpress.XtraReports.Web.ClientControls.LoggerService.Initialize((ex, message) => System.Diagnostics.Debug.WriteLine("[{0}]: Exception occurred. Message: '{1}'. Exception Details:\r\n{2}", DateTime.Now, message, ex)); DevExpress.Web.ASPxWebControl.CallbackError += new EventHandler(Application_Error); } void Application_End(object sender, EventArgs e) { // Code that runs on application shutdown } void Application_Error(object sender, EventArgs e) { // Code that runs when an unhandled error occurs } void Session_Start(object sender, EventArgs e) { // Code that runs when a new session is started } void Session_End(object sender, EventArgs e) { // Code that runs when a session ends. // Note: The Session_End event is raised only when the sessionstate mode // is set to InProc in the Web.config file. If session mode is set to StateServer // or SQLServer, the event is not raised. } } }
WebForms/VB/Reporting_ObjectDS_WebForms/Global.asax.vb
Visual Basic
Imports System Imports System.IO Imports System.Web Imports DevExpress.XtraReports.Web Imports Reporting_ObjectDS_WebForms.Services Namespace Reporting_ObjectDS_WebForms Public Class Global_asax Inherits System.Web.HttpApplication Private Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) System.Web.Routing.RouteTable.Routes.MapPageRoute("defaultRoute", "", "~/Default.aspx") DevExpress.XtraReports.Configuration.Settings.Default.UserDesignerOptions.DataBindingMode = DevExpress.XtraReports.UI.DataBindingMode.Expressions DevExpress.XtraReports.Web.WebDocumentViewer.Native.WebDocumentViewerBootstrapper.SessionState = System.Web.SessionState.SessionStateBehavior.Default DevExpress.XtraReports.Web.QueryBuilder.Native.QueryBuilderBootstrapper.SessionState = System.Web.SessionState.SessionStateBehavior.Default DevExpress.XtraReports.Web.ReportDesigner.Native.ReportDesignerBootstrapper.SessionState = System.Web.SessionState.SessionStateBehavior.Default DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension.RegisterExtensionGlobal(New CustomReportStorageWebExtension(Server.MapPath("/Reports"))) DevExpress.XtraReports.Web.ReportDesigner.DefaultReportDesignerContainer.RegisterObjectDataSourceConstructorFilterService(Of CustomObjectDataSourceConstructorFilterService)() DevExpress.XtraReports.Web.WebDocumentViewer.DefaultWebDocumentViewerContainer.Register(Of DevExpress.XtraReports.Web.WebDocumentViewer.IWebDocumentViewerReportResolver, CustomWebDocumentViewerReportResolver)() DevExpress.XtraReports.Web.ReportDesigner.DefaultReportDesignerContainer.RegisterObjectDataSourceWizardTypeProvider(Of ObjectDataSourceWizardCustomTypeProvider)() System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol Or System.Net.SecurityProtocolType.Tls12 ASPxReportDesigner.StaticInitialize() DevExpress.XtraReports.Web.ClientControls.LoggerService.Initialize(AddressOf ProcessException) AddHandler DevExpress.Web.ASPxWebControl.CallbackError, AddressOf Application_Error End Sub Private Sub ProcessException(ex As Exception, message As String) Debug.WriteLine("[{0}]: Exception occurred. Message: '{1}'. Exception Details:" & ControlChars.CrLf & "{2}", Date.Now, message, ex) End Sub Private Sub Application_End(ByVal sender As Object, ByVal e As EventArgs) ' Code that runs on application shutdown End Sub Private Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs) ' Code that runs when an unhandled error occurs End Sub Private Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs) ' Code that runs when a new session is started End Sub Private Sub Session_End(ByVal sender As Object, ByVal e As EventArgs) ' Code that runs when a session ends. ' Note: The Session_End event is raised only when the sessionstate mode ' is set to InProc in the Web.config file. If session mode is set to StateServer ' or SQLServer, the event is not raised. End Sub End Class End Namespace
MVC/CS/Reporting_ObjectDS_Mvc/ObjectDataSource/EmployeesDataSource.cs
C#
using System.Collections.Generic; using System.ComponentModel; using System.Linq; namespace Reporting_ObjectDS_Mvc.Employees { [DisplayName("Employees")] public class EmployeeList { public EmployeeList() { Items = InitializeList(); } public EmployeeList(int noOfItems) { Items = InitializeList().GetRange(1, noOfItems); } public List<DataItem> Items { get; set; } public List<DataItem> InitializeList() { return new List<DataItem>() { new DataItem(1, 101, "Andrew Fuller", "Dr.", "Vice President, Sales"), new DataItem(1, 102, "Anne Dodsworth", "Ms.", "Sales Representative"), new DataItem(1, 103, "Michael Suyama", "Mr.", "Sales Representative"), new DataItem(1, 104, "Janet Leverling", "Ms.", "Sales Representative"), new DataItem(1, 105, "Elliot Komaroff", "Dr.", "Sales Coordinator"), new DataItem(2, 201, "Nancy Davolio", "Ms.", "Sales Representative"), new DataItem(2, 202, "Steven Buchanan", "Mr.", "Sales Manager"), new DataItem(2, 203, "Laura Callahan", "Ms.", "Sales Coordinator"), new DataItem(3, 301, "Frédérique Citeaux", "Mr.", "Sales Coordinator"), new DataItem(3, 302, "Laurence Lebihan", "Mr.", "Sales Representative"), new DataItem(3, 303, "Elizabeth Lincoln", "Ms.", "Sales Manager"), new DataItem(3, 304, "Yang Wang", "Mr.", "Sales Representative"), new DataItem(4, 401, "Antonio Moreno", "Mr.", "Sales Representative"), new DataItem(4, 402, "Thomas Hardy", "Mr.", "Sales Representative"), new DataItem(4, 403, "Christina Berglund", "Ms.", "Sales Manager"), new DataItem(5, 501, "Alejandra Camino", "Ms.", "Sales Representative"), new DataItem(5, 502, "Matti Karttunen", "Mr.", "Sales Representative"), new DataItem(5, 503, "Rita Müller", "Mrs.", "Sales Representative"), }; } public List<DataItem> GetData(int noOfItems) { List<DataItem> revertList = new List<DataItem>(Items); revertList.Reverse(); return revertList.Take(noOfItems).ToList(); } } public class DataItem { public DataItem(int floor, int office, string personName, string titleOfCourtesy, string title) { Floor = floor; Office = office; PersonName = personName; TitleOfCourtesy = titleOfCourtesy; Title = title; } public int Floor { get; set; } public int Office { get; set; } public string PersonName { get; set; } public string TitleOfCourtesy { get; set; } public string Title { get; set; } } }
MVC/CS/Reporting_ObjectDS_Mvc/Services/CustomWebDocumentViewerReportResolver.cs
C#
using DevExpress.DataAccess.ObjectBinding; using DevExpress.XtraReports.UI; using DevExpress.XtraReports.Web.WebDocumentViewer; namespace Reporting_ObjectDS_Mvc { public class CustomWebDocumentViewerReportResolver : IWebDocumentViewerReportResolver { public XtraReport Resolve(string reportEntry) { if (reportEntry.StartsWith("EmployeeReport")) { XtraReport rep = CreateReport(reportEntry); rep.DataSource = CreateObjectDataSource(reportEntry); return rep; } return new XtraReport(); } private object CreateObjectDataSource(string reportName) { if (reportName == "EmployeeReport") { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(Employees.EmployeeList); dataSource.Constructor = ObjectConstructorInfo.Default; dataSource.DataMember = "Items"; return dataSource; } else if (reportName.EndsWith("7")) { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(Employees.EmployeeList); // Specify the parameter's default value. var parameter = new Parameter("noOfItems", typeof(int), 7); dataSource.Constructor = new ObjectConstructorInfo(parameter); dataSource.DataMember = "Items"; return dataSource; } else if (reportName.EndsWith("Parameter")) { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(Employees.EmployeeList); // Map data source parameter to report's parameter. var parameter = new Parameter() { Name = "noOfItems", Type = typeof(DevExpress.DataAccess.Expression), Value = new DevExpress.DataAccess.Expression("?parameterNoOfItems", typeof(int)) }; dataSource.Constructor = new ObjectConstructorInfo(parameter); dataSource.DataMember = "Items"; return dataSource; } else { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(Employees.EmployeeList); var parameterNoOfItems = new Parameter("noOfItems", typeof(int), 12); dataSource.Parameters.Add(parameterNoOfItems); dataSource.DataMember = "GetData"; dataSource.Constructor = ObjectConstructorInfo.Default; return dataSource; } } private XtraReport CreateReport(string reportEntry) { if (reportEntry.Contains("Parameter")) { XtraReport report = new PredefinedReports.EmployeeReport(); DevExpress.XtraReports.Parameters.Parameter param = new DevExpress.XtraReports.Parameters.Parameter() { Name = "parameterNoOfItems", Type = typeof(int), Value = 5 }; param.Description = "Number of Items"; report.Parameters.Add(param); report.RequestParameters = false; return report; } else return new PredefinedReports.EmployeeReport(); } } }
MVC/CS/Reporting_ObjectDS_Mvc/Services/CustomReportStorageWebExtension.cs
C#
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 Reporting_ObjectDS_Mvc.PredefinedReports; namespace Reporting_ObjectDS_Mvc.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 or not it is possible to store a report by a given URL. // For instance, make the CanSetData method return false for reports that should be read-only in your storage. // This method is called only for valid URLs (i.e., if the IsValidUrl method returned true) before the SetData method is called. return true; } public override bool IsValidUrl(string url) { // Determines whether or not the URL passed to the current Report Storage is valid. // For instance, implement your own logic to prohibit URLs that contain white spaces or some other special characters. // This method is called before the CanSetData and GetData methods. return Path.GetFileName(url) == url; } public override byte[] GetData(string url) { // Returns report layout data stored in a Report Storage using the specified URL. // This method is called only for valid URLs after the IsValidUrl method is called. 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 of the existing report URLs and display names. // This method is called when running the Report Designer, // before the Open Report and Save Report dialogs are shown and after a new report is saved to a storage. 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) { // Stores the specified report to a Report Storage using the specified URL. // This method is called only after the IsValidUrl and CanSetData methods are called. 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) { // Stores the specified report using a new URL. // The IsValidUrl and CanSetData methods are never called before this method. // You can validate and correct the specified URL directly in the SetNewData method implementation // and return the resulting URL used to save a report in your storage. SetData(report, defaultUrl); return defaultUrl; } } }
MVC/CS/Reporting_ObjectDS_Mvc/Services/CustomObjectDataSourceConstructorFilterService.cs
C#
using DevExpress.DataAccess.Web; using Reporting_ObjectDS_Mvc.Employees; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; public class CustomObjectDataSourceConstructorFilterService : IObjectDataSourceConstructorFilterService { public IEnumerable<ConstructorInfo> Filter(Type dataSourceType, IEnumerable<ConstructorInfo> constructors) { if (dataSourceType == typeof(EmployeeList)) return constructors; else return constructors.Where(x => x.GetParameters().Length > 0); } }
MVC/CS/Reporting_ObjectDS_Mvc/Services/ObjectDataSourceWizardCustomTypeProvider.cs
C#
using DevExpress.DataAccess.Web; using System; using System.Collections.Generic; namespace Reporting_ObjectDS_Mvc.Services { public class ObjectDataSourceWizardCustomTypeProvider : IObjectDataSourceWizardTypeProvider { public IEnumerable<Type> GetAvailableTypes(string context) { return new[] { typeof(Employees.EmployeeList) }; } } }
MVC/CS/Reporting_ObjectDS_Mvc/Global.asax.cs
C#
using System; using System.Web.Http; using System.Web.Mvc; using System.Web.Routing; using DevExpress.Web.Mvc; using Reporting_ObjectDS_Mvc.Employees; using Reporting_ObjectDS_Mvc.Services; namespace Reporting_ObjectDS_Mvc { // Note: For instructions on enabling IIS6 or IIS7 classic mode, // visit http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { DevExpress.XtraReports.Configuration.Settings.Default.UserDesignerOptions.DataBindingMode = DevExpress.XtraReports.UI.DataBindingMode.Expressions; DevExpress.XtraReports.Web.WebDocumentViewer.Native.WebDocumentViewerBootstrapper.SessionState = System.Web.SessionState.SessionStateBehavior.Default; DevExpress.XtraReports.Web.QueryBuilder.Native.QueryBuilderBootstrapper.SessionState = System.Web.SessionState.SessionStateBehavior.Default; DevExpress.XtraReports.Web.ReportDesigner.Native.ReportDesignerBootstrapper.SessionState = System.Web.SessionState.SessionStateBehavior.Default; DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension.RegisterExtensionGlobal(new CustomReportStorageWebExtension(Server.MapPath("/Reports"))); DevExpress.XtraReports.Web.WebDocumentViewer.DefaultWebDocumentViewerContainer.Register<DevExpress.XtraReports.Web.WebDocumentViewer.IWebDocumentViewerReportResolver, CustomWebDocumentViewerReportResolver>(); DevExpress.XtraReports.Web.ReportDesigner.DefaultReportDesignerContainer.RegisterObjectDataSourceWizardTypeProvider<ObjectDataSourceWizardCustomTypeProvider>(); DevExpress.XtraReports.Web.ReportDesigner.DefaultReportDesignerContainer.RegisterObjectDataSourceConstructorFilterService<CustomObjectDataSourceConstructorFilterService>(); DevExpress.Utils.DeserializationSettings.RegisterTrustedClass(typeof(EmployeeList)); System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12; MVCxReportDesigner.StaticInitialize(); DevExpress.XtraReports.Web.ClientControls.LoggerService.Initialize((ex, message) => System.Diagnostics.Debug.WriteLine("[{0}]: Exception occurred. Message: '{1}'. Exception Details:\r\n{2}", DateTime.Now, message, ex)); AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); ModelBinders.Binders.DefaultBinder = new DevExpress.Web.Mvc.DevExpressEditorsBinder(); DevExpress.Web.ASPxWebControl.CallbackError += Application_Error; } protected void Application_Error(object sender, EventArgs e) { Exception exception = System.Web.HttpContext.Current.Server.GetLastError(); //TODO: Handle Exception } } }
AspNetCore/Reporting_ObjectDS_AspNetCore/DataSources/EmployeesDataSource.cs
C#
using System.Collections.Generic; using System.ComponentModel; using System.Linq; namespace Reporting_ObjectDS_AspNetCore { [DisplayName("Employees")] public class EmployeeList { public EmployeeList() { Items = InitializeList(); } public EmployeeList(int noOfItems) { Items = InitializeList().GetRange(1, noOfItems); } public List<DataItem> Items { get; set; } public List<DataItem> InitializeList() { return new List<DataItem>() { new DataItem(1, 101, "Andrew Fuller", "Dr.", "Vice President, Sales"), new DataItem(1, 102, "Anne Dodsworth", "Ms.", "Sales Representative"), new DataItem(1, 103, "Michael Suyama", "Mr.", "Sales Representative"), new DataItem(1, 104, "Janet Leverling", "Ms.", "Sales Representative"), new DataItem(1, 105, "Elliot Komaroff", "Dr.", "Sales Coordinator"), new DataItem(2, 201, "Nancy Davolio", "Ms.", "Sales Representative"), new DataItem(2, 202, "Steven Buchanan", "Mr.", "Sales Manager"), new DataItem(2, 203, "Laura Callahan", "Ms.", "Sales Coordinator"), new DataItem(3, 301, "Frédérique Citeaux", "Mr.", "Sales Coordinator"), new DataItem(3, 302, "Laurence Lebihan", "Mr.", "Sales Representative"), new DataItem(3, 303, "Elizabeth Lincoln", "Ms.", "Sales Manager"), new DataItem(3, 304, "Yang Wang", "Mr.", "Sales Representative"), new DataItem(4, 401, "Antonio Moreno", "Mr.", "Sales Representative"), new DataItem(4, 402, "Thomas Hardy", "Mr.", "Sales Representative"), new DataItem(4, 403, "Christina Berglund", "Ms.", "Sales Manager"), new DataItem(5, 501, "Alejandra Camino", "Ms.", "Sales Representative"), new DataItem(5, 502, "Matti Karttunen", "Mr.", "Sales Representative"), new DataItem(5, 503, "Rita Müller", "Mrs.", "Sales Representative"), }; } public List<DataItem> GetData(int noOfItems) { List<DataItem> revertList = new List<DataItem>(Items); revertList.Reverse(); return revertList.Take(noOfItems).ToList(); } } public class DataItem { public DataItem(int floor, int office, string personName, string titleOfCourtesy, string title) { Floor = floor; Office = office; PersonName = personName; TitleOfCourtesy = titleOfCourtesy; Title = title; } public int Floor { get; set; } public int Office { get; set; } public string PersonName { get; set; } public string TitleOfCourtesy { get; set; } public string Title { get; set; } } }
AspNetCore/Reporting_ObjectDS_AspNetCore/Services/CustomWebDocumentViewerReportResolver.cs
C#
using DevExpress.DataAccess.ObjectBinding; using DevExpress.XtraReports.UI; using DevExpress.XtraReports.Web.WebDocumentViewer; namespace Reporting_ObjectDS_AspNetCore { public class CustomWebDocumentViewerReportResolver : IWebDocumentViewerReportResolver { public XtraReport Resolve(string reportEntry) { if (reportEntry.StartsWith("EmployeeReport")) { XtraReport rep = CreateReport(reportEntry); rep.DataSource = CreateObjectDataSource(reportEntry); return rep; } return new XtraReport(); } private object CreateObjectDataSource(string reportName) { if (reportName == "EmployeeReport") { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(EmployeeList); dataSource.Constructor = ObjectConstructorInfo.Default; dataSource.DataMember = "Items"; return dataSource; } else if (reportName.EndsWith("7")) { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(EmployeeList); // Specify the parameter's default value. var parameter = new Parameter("noOfItems", typeof(int), 7); dataSource.Constructor = new ObjectConstructorInfo(parameter); dataSource.DataMember = "Items"; return dataSource; } else if (reportName.EndsWith("Parameter")) { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(EmployeeList); // Map data source parameter to report's parameter. var parameter = new Parameter() { Name = "noOfItems", Type = typeof(DevExpress.DataAccess.Expression), Value = new DevExpress.DataAccess.Expression("?parameterNoOfItems", typeof(int)) }; dataSource.Constructor = new ObjectConstructorInfo(parameter); dataSource.DataMember = "Items"; return dataSource; } else { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(EmployeeList); var parameterNoOfItems = new Parameter("noOfItems", typeof(int), 12); dataSource.Parameters.Add(parameterNoOfItems); dataSource.Constructor = ObjectConstructorInfo.Default; dataSource.DataMember = "GetData"; return dataSource; } } private XtraReport CreateReport(string reportEntry) { if (reportEntry.Contains("Parameter")) { XtraReport report = new PredefinedReports.EmployeeReport(); DevExpress.XtraReports.Parameters.Parameter param = new DevExpress.XtraReports.Parameters.Parameter() { Name = "parameterNoOfItems", Type = typeof(int), Value = 5 }; param.Description = "Number of Items"; report.Parameters.Add(param); report.RequestParameters = false; return report; } else return new PredefinedReports.EmployeeReport(); } } }
AspNetCore/Reporting_ObjectDS_AspNetCore/Services/CustomReportStorageWebExtension.cs
C#
using DevExpress.XtraReports.UI; using DevExpress.XtraReports.Web.Extensions; using Microsoft.AspNetCore.Hosting; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace Reporting_ObjectDS_AspNetCore.Services { public class CustomReportStorageWebExtension : ReportStorageWebExtension { readonly string ReportDirectory; const string FileExtension = ".repx"; public CustomReportStorageWebExtension(IWebHostEnvironment env) { ReportDirectory = Path.Combine(env.ContentRootPath, "Reports"); if (!Directory.Exists(ReportDirectory)) { Directory.CreateDirectory(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 or not it is possible to store a report by a given URL. // For instance, make the CanSetData method return false for reports that should be read-only in your storage. // This method is called only for valid URLs (i.e., if the IsValidUrl method returned true) before the SetData method is called. return true; } public override bool IsValidUrl(string url) { // Determines whether or not the URL passed to the current Report Storage is valid. // For instance, implement your own logic to prohibit URLs that contain white spaces or some other special characters. // This method is called before the CanSetData and GetData methods. return Path.GetFileName(url) == url; } public override byte[] GetData(string url) { // Returns report layout data stored in a Report Storage using the specified URL. // This method is called only for valid URLs after the IsValidUrl method is called. 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 ex) { throw new DevExpress.XtraReports.Web.ClientControls.FaultException("Could not get report data.", ex); } throw new DevExpress.XtraReports.Web.ClientControls.FaultException(string.Format("Could not find report '{0}'.", url)); } public override Dictionary<string, string> GetUrls() { // Returns a dictionary of the existing report URLs and display names. // This method is called when running the Report Designer, // before the Open Report and Save Report dialogs are shown and after a new report is saved to a storage. 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) { // Stores the specified report to a Report Storage using the specified URL. // This method is called only after the IsValidUrl and CanSetData methods are called. if(!IsWithinReportsFolder(url, ReportDirectory)) throw new DevExpress.XtraReports.Web.ClientControls.FaultException("Invalid report name."); report.SaveLayoutToXml(Path.Combine(ReportDirectory, url + FileExtension)); } public override string SetNewData(XtraReport report, string defaultUrl) { // Stores the specified report using a new URL. // The IsValidUrl and CanSetData methods are never called before this method. // You can validate and correct the specified URL directly in the SetNewData method implementation // and return the resulting URL used to save a report in your storage. SetData(report, defaultUrl); return defaultUrl; } } }
AspNetCore/Reporting_ObjectDS_AspNetCore/Services/CustomObjectDataSourceConstructorFilterService.cs
C#
using DevExpress.DataAccess.Web; using Reporting_ObjectDS_AspNetCore; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; public class CustomObjectDataSourceConstructorFilterService : IObjectDataSourceConstructorFilterService { public IEnumerable<ConstructorInfo> Filter(Type dataSourceType, IEnumerable<ConstructorInfo> constructors) { if (dataSourceType == typeof(EmployeeList)) return constructors; else return constructors.Where(x => x.GetParameters().Length > 0); } }
AspNetCore/Reporting_ObjectDS_AspNetCore/Services/ObjectDataSourceWizardCustomTypeProvider.cs
C#
using DevExpress.DataAccess.Web; using System; using System.Collections.Generic; namespace Reporting_ObjectDS_AspNetCore.Services { public class ObjectDataSourceWizardCustomTypeProvider : IObjectDataSourceWizardTypeProvider { public IEnumerable<Type> GetAvailableTypes(string context) { return new[] { typeof(EmployeeList) }; } } }
AspNetCore/Reporting_ObjectDS_AspNetCore/Startup.cs
C#
using DevExpress.AspNetCore; using DevExpress.AspNetCore.Reporting; using DevExpress.XtraReports.Web.Extensions; using DevExpress.XtraReports.Web.WebDocumentViewer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Reporting_ObjectDS_AspNetCore.Services; namespace Reporting_ObjectDS_AspNetCore { public class Startup { public Startup(IConfiguration configuration, IWebHostEnvironment hostingEnvironment) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddDevExpressControls(); services.AddScoped<ReportStorageWebExtension, CustomReportStorageWebExtension>(); services.AddTransient<IWebDocumentViewerReportResolver, CustomWebDocumentViewerReportResolver>(); services .AddControllersWithViews(); services.ConfigureReportingServices(configurator => { configurator.ConfigureReportDesigner(designerConfigurator => { designerConfigurator.RegisterDataSourceWizardConfigFileConnectionStringsProvider(); designerConfigurator.RegisterObjectDataSourceWizardTypeProvider<ObjectDataSourceWizardCustomTypeProvider>(); designerConfigurator.RegisterObjectDataSourceConstructorFilterService<CustomObjectDataSourceConstructorFilterService>(); }); configurator.ConfigureWebDocumentViewer(viewerConfigurator => { viewerConfigurator.UseCachedReportSourceBuilder(); }); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) { DevExpress.Utils.DeserializationSettings.RegisterTrustedClass(typeof(EmployeeList)); DevExpress.XtraReports.Configuration.Settings.Default.UserDesignerOptions.DataBindingMode = DevExpress.XtraReports.UI.DataBindingMode.Expressions; app.UseDevExpressControls(); System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12; if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } } }
Blazor/Reporting_ObjectDS_Blazor/Data/EmployeesDataSource.cs
C#
using System.Collections.Generic; using System.ComponentModel; using System.Linq; namespace Reporting_ObjectDS_Blazor { [DisplayName("Employees")] public class EmployeeList { public EmployeeList() { Items = InitializeList(); } public EmployeeList(int noOfItems) { Items = InitializeList().GetRange(1, noOfItems); } public List<DataItem> Items { get; set; } public List<DataItem> InitializeList() { return new List<DataItem>() { new DataItem(1, 101, "Andrew Fuller", "Dr.", "Vice President, Sales"), new DataItem(1, 102, "Anne Dodsworth", "Ms.", "Sales Representative"), new DataItem(1, 103, "Michael Suyama", "Mr.", "Sales Representative"), new DataItem(1, 104, "Janet Leverling", "Ms.", "Sales Representative"), new DataItem(1, 105, "Elliot Komaroff", "Dr.", "Sales Coordinator"), new DataItem(2, 201, "Nancy Davolio", "Ms.", "Sales Representative"), new DataItem(2, 202, "Steven Buchanan", "Mr.", "Sales Manager"), new DataItem(2, 203, "Laura Callahan", "Ms.", "Sales Coordinator"), new DataItem(3, 301, "Frédérique Citeaux", "Mr.", "Sales Coordinator"), new DataItem(3, 302, "Laurence Lebihan", "Mr.", "Sales Representative"), new DataItem(3, 303, "Elizabeth Lincoln", "Ms.", "Sales Manager"), new DataItem(3, 304, "Yang Wang", "Mr.", "Sales Representative"), new DataItem(4, 401, "Antonio Moreno", "Mr.", "Sales Representative"), new DataItem(4, 402, "Thomas Hardy", "Mr.", "Sales Representative"), new DataItem(4, 403, "Christina Berglund", "Ms.", "Sales Manager"), new DataItem(5, 501, "Alejandra Camino", "Ms.", "Sales Representative"), new DataItem(5, 502, "Matti Karttunen", "Mr.", "Sales Representative"), new DataItem(5, 503, "Rita Müller", "Mrs.", "Sales Representative"), }; } public List<DataItem> GetData(int noOfItems) { List<DataItem> revertList = new List<DataItem>(Items); revertList.Reverse(); return revertList.Take(noOfItems).ToList(); } } public class DataItem { public DataItem(int floor, int office, string personName, string titleOfCourtesy, string title) { Floor = floor; Office = office; PersonName = personName; TitleOfCourtesy = titleOfCourtesy; Title = title; } public int Floor { get; set; } public int Office { get; set; } public string PersonName { get; set; } public string TitleOfCourtesy { get; set; } public string Title { get; set; } } }
Blazor/Reporting_ObjectDS_Blazor/Services/CustomWebDocumentViewerReportResolver.cs
C#
using DevExpress.DataAccess.ObjectBinding; using DevExpress.XtraReports.UI; using DevExpress.XtraReports.Web.Extensions; using DevExpress.XtraReports.Web.WebDocumentViewer; using Reporting_ObjectDS_Blazor.Reports; using System.IO; namespace Reporting_ObjectDS_Blazor { public class CustomWebDocumentViewerReportResolver : IWebDocumentViewerReportResolver { readonly ReportStorageWebExtension reportStorageWebExtension; public CustomWebDocumentViewerReportResolver(ReportStorageWebExtension reportStorageWebExtension) { this.reportStorageWebExtension = reportStorageWebExtension; } public XtraReport Resolve(string reportEntry) { string reportName = reportEntry.Substring(0, reportEntry.IndexOf("?") == -1 ? reportEntry.Length : reportEntry.IndexOf("?")); var reportLayout = reportStorageWebExtension.GetData(reportEntry); if (reportLayout == null) return new XtraReport(); using (var ms = new MemoryStream(reportLayout)) { var report = XtraReport.FromXmlStream(ms); report.DataSource = CreateObjectDataSource(reportName); return report; } } private object CreateObjectDataSource(string reportName) { if (reportName == "EmployeeReport") { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(EmployeeList); dataSource.Constructor = ObjectConstructorInfo.Default; dataSource.DataMember = "Items"; return dataSource; } else if (reportName == "EmployeeReport7") { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(EmployeeList); // Specify the parameter's default value. var parameter = new Parameter("noOfItems", typeof(int), 7); dataSource.Constructor = new ObjectConstructorInfo(parameter); dataSource.DataMember = "Items"; return dataSource; } else if (reportName == "EmployeeReportXtraParameter") { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(EmployeeList); // Map data source parameter to report's parameter. var parameter = new Parameter() { Name = "noOfItems", Type = typeof(DevExpress.DataAccess.Expression), Value = new DevExpress.DataAccess.Expression("?parameterNoOfItems", typeof(int)) }; dataSource.Constructor = new ObjectConstructorInfo(parameter); dataSource.DataMember = "Items"; return dataSource; } else { ObjectDataSource dataSource = new ObjectDataSource(); dataSource.Name = "EmployeeObjectDS"; dataSource.DataSource = typeof(EmployeeList); var parameterNoOfItems = new Parameter("noOfItems", typeof(int), 12); dataSource.Parameters.Add(parameterNoOfItems); dataSource.DataMember = "GetData"; dataSource.Constructor = ObjectConstructorInfo.Default; return dataSource; } } //private XtraReport AddParameterToReport(XtraReport report) //{ // DevExpress.XtraReports.Parameters.Parameter param = // new DevExpress.XtraReports.Parameters.Parameter() // { // Name = "parameterNoOfItems", // Type = typeof(int), // Value = 5 // }; // param.Description = "Number of Items"; // report.Parameters.Add(param); // report.RequestParameters = false; // return report; //} } }
Blazor/Reporting_ObjectDS_Blazor/Services/CustomReportStorageWebExtension.cs
C#
using System; using Microsoft.AspNetCore.Hosting; using System.Collections.Generic; using System.IO; using System.Linq; using DevExpress.XtraReports.UI; using DevExpress.XtraReports.Web.ClientControls; using DevExpress.XtraReports.Parameters; namespace Reporting_ObjectDS_Blazor { public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension { readonly string ReportDirectory; const string FileExtension = ".repx"; public CustomReportStorageWebExtension(IWebHostEnvironment env) { ReportDirectory = Path.Combine(env.ContentRootPath, "Reports"); if (!Directory.Exists(ReportDirectory)) { Directory.CreateDirectory(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 it is possible to store a report by a given URL. // For instance, make the CanSetData method return false for reports that should be read-only in your storage. // This method is called only for valid URLs (for example, if the IsValidUrl method returns true) before the SetData method is called. return true; } public override bool IsValidUrl(string url) { // Determines whether the URL passed to the current Report Storage is valid. // For instance, implement your own logic to prohibit URLs that contain white spaces or other special characters. // This method is called before the CanSetData and GetData methods. return Path.GetFileName(url) == url; } public override byte[] GetData(string url) { string reportName = url.Substring(0, url.IndexOf("?") == -1 ? url.Length : url.IndexOf("?")); var query = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(url.Substring(url.IndexOf("?")+1)); var parameterDictionary = query.SelectMany(x => x.Value, (col, value) => new KeyValuePair<string, string>(col.Key, value)); bool needExtraParameter = reportName.Contains("XtraParameter"); if (reportName.StartsWith("EmployeeReport")) reportName = "EmployeeReport"; try { if (Directory.EnumerateFiles(ReportDirectory). Select(Path.GetFileNameWithoutExtension). Contains(reportName)) { string path = Path.Combine(ReportDirectory, reportName + FileExtension); if (parameterDictionary.Count() == 0) return File.ReadAllBytes(path); else { using (MemoryStream ms = new MemoryStream()) { XtraReport report = XtraReport.FromXmlFile(path); foreach (KeyValuePair<string, string> entry in parameterDictionary) { if (report.Parameters[entry.Key] != null) { report.Parameters[entry.Key].Value = entry.Value; report.Parameters[entry.Key].Visible = true; } } if (needExtraParameter) { Parameter param = new Parameter(){ Name = "parameterNoOfItems", Type = typeof(int), Value = 5 }; param.Description = "Number of Items"; report.Parameters.Add(param); } report.RequestParameters = false; report.SaveLayoutToXml(ms); return ms.ToArray(); } } } } catch (Exception ex) { throw new FaultException("Could not get report data.", ex); } throw new FaultException(string.Format("Could not find report '{0}'.", url)); } public override Dictionary<string, string> GetUrls() { // Returns a dictionary of the existing report URLs and display names. // This method is called when running the Report Designer, // before the Open Report and Save Report dialogs are shown and after a new report is saved to storage. return Directory.GetFiles(ReportDirectory, "*" + FileExtension) .Select(Path.GetFileNameWithoutExtension) .ToDictionary<string, string>(x => x); } public override void SetData(XtraReport report, string url) { // Stores the specified report to a Report Storage using the specified URL. // This method is called only after the IsValidUrl and CanSetData methods are called. if (!IsWithinReportsFolder(url, ReportDirectory)) throw new DevExpress.XtraReports.Web.ClientControls.FaultException("Invalid report name."); report.SaveLayoutToXml(Path.Combine(ReportDirectory, url + FileExtension)); } public override string SetNewData(XtraReport report, string defaultUrl) { // Stores the specified report using a new URL. // The IsValidUrl and CanSetData methods are never called before this method. // You can validate and correct the specified URL directly in the SetNewData method implementation // and return the resulting URL used to save a report in your storage. SetData(report, defaultUrl); return defaultUrl; } } }
Blazor/Reporting_ObjectDS_Blazor/Services/ObjectDataSourceWizardCustomTypeProvider.cs
C#
using DevExpress.DataAccess.Web; using System; using System.Collections.Generic; namespace Reporting_ObjectDS_Blazor { public class ObjectDataSourceWizardCustomTypeProvider : IObjectDataSourceWizardTypeProvider { public IEnumerable<Type> GetAvailableTypes(string context) { return new[] { typeof(EmployeeList) }; } } }
Blazor/Reporting_ObjectDS_Blazor/Startup.cs
C#
using DevExpress.AspNetCore.Reporting; using DevExpress.Blazor.Reporting; using DevExpress.XtraReports.Web.Extensions; using DevExpress.XtraReports.Web.WebDocumentViewer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace Reporting_ObjectDS_Blazor { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddServerSideBlazor(); services.AddDevExpressBlazorReporting(); services.AddScoped<ReportStorageWebExtension, CustomReportStorageWebExtension>(); services.AddTransient<IWebDocumentViewerReportResolver, CustomWebDocumentViewerReportResolver>(); services.ConfigureReportingServices(configurator => { configurator.ConfigureReportDesigner(designerConfigurator => { designerConfigurator.RegisterObjectDataSourceWizardTypeProvider<ObjectDataSourceWizardCustomTypeProvider>(); }); DevExpress.Utils.DeserializationSettings.RegisterTrustedClass(typeof(EmployeeList)); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseDevExpressBlazorReporting(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); endpoints.MapControllers(); }); } } }

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.