Example T190370
Visible to All Users

Reporting for ASP.NET MVC - End-User Report Designer and Report Database Storage

This example includes Web End-User Report Designer that uses an SQLite database to store reports.

The Web Report Designer uses the ReportStorageWebExtension to manage reports.

After you run the application, select a report in the list box. The list box displays the names of the reports stored in the database:

Click Run Designer to invoke the End-User Report Designer for the selected report. You can edit a report, save it to a database, and exit Designer to return to the report catalog.

Files to Review

Documentation

More Examples

Example Code

Mvc_DbStorage_Sample/Controllers/HomeController.cs
C#
using System; using System.Collections.Generic; using System.Linq; using System.Web; using DevExpress.DataAccess.Sql; using Mvc_DbStorage_Sample.Services.DAL; using System.Web.Mvc; using DevExpress.Xpo; namespace Mvc_DbStorage_Sample.Controllers { public class HomeController : Controller { [HttpGet] public ActionResult Index() { using (var session = SessionFactory.Create()) { var reports = session.Query<ReportEntity>() .Select(x => new ReportModel { Url = x.Url }) .ToArray(); var firstReport = reports.FirstOrDefault(); var model = new IndexModel { SelectedReportUrl = firstReport != null ? firstReport.Url : String.Empty, Reports = reports }; return View("Index", model); } } [HttpPost] public ActionResult Delete(string url) { using (var session = SessionFactory.Create()) { var report = session.GetObjectByKey<ReportEntity>(url); session.Delete(report); session.CommitChanges(); } return Index(); } [HttpPost] public ActionResult Design(string url) { return View("Designer", new DesignModel { Url = url, DataSource = CreateSqlDataSource() }); } SqlDataSource CreateSqlDataSource() { SqlDataSource ds = new SqlDataSource("NWindConnectionString"); ds.Name = "NWind"; SelectQuery categoriesQuery = SelectQueryFluentBuilder.AddTable("Categories").SelectAllColumns().Build("Categories"); ds.Queries.Add(categoriesQuery); ds.RebuildResultSchema(); return ds; } } }
Mvc_DbStorage_Sample_VB/Controllers/HomeController.vb
Visual Basic
Imports System Imports System.Collections.Generic Imports System.Linq Imports System.Web Imports DevExpress.DataAccess.Sql Imports System.Web.Mvc Imports DevExpress.Xpo Imports Mvc_DbStorage_Sample_VB.Services.DAL Namespace Mvc_DbStorage_Sample.Controllers Public Class HomeController Inherits Controller <HttpGet> Public Function Index() As ActionResult Using session = SessionFactory.Create() Dim reports = session.Query(Of ReportEntity)().Select(Function(x) New ReportModel With {.Url = x.Url}).ToArray() Dim firstReport = reports.FirstOrDefault() Dim model = New IndexModel With { .SelectedReportUrl = If(firstReport IsNot Nothing, firstReport.Url, String.Empty), .Reports = reports } Return View("Index", model) End Using End Function <HttpPost> Public Function Delete(ByVal url As String) As ActionResult Using session = SessionFactory.Create() Dim report = session.GetObjectByKey(Of ReportEntity)(url) session.Delete(report) session.CommitChanges() End Using Return Index() End Function <HttpPost> Public Function Design(ByVal url As String) As ActionResult Return View("Designer", New DesignModel With { .Url = url, .DataSource = CreateSqlDataSource() }) End Function Private Function CreateSqlDataSource() As SqlDataSource Dim ds As New SqlDataSource("NWindConnectionString") ds.Name = "NWind" Dim categoriesQuery As SelectQuery = SelectQueryFluentBuilder.AddTable("Categories").SelectAllColumns().Build("Categories") ds.Queries.Add(categoriesQuery) ds.RebuildResultSchema() Return ds End Function End Class End Namespace
Mvc_DbStorage_Sample/Services/CustomReportStorageWebExtension.cs
C#
using System; using System.Linq; using System.Collections.Generic; using DevExpress.Xpo; using DevExpress.XtraReports.UI; using DevExpress.XtraReports.Web.Extensions; using System.IO; using Mvc_DbStorage_Sample.Services.DAL; namespace Mvc_DbStorage_Sample.Services { public class CustomReportStorageWebExtension : ReportStorageWebExtension { public override bool CanSetData(string url) { // Check if the URL is available in the report storage. using (var session = SessionFactory.Create()) { return session.GetObjectByKey<ReportEntity>(url) != null; } } public override byte[] GetData(string url) { // Get the report data from the storage. using (var session = SessionFactory.Create()) { var report = session.GetObjectByKey<ReportEntity>(url); return report.Layout; } } public override Dictionary<string, string> GetUrls() { // Get URLs and display names for all reports available in the storage using (var session = SessionFactory.Create()) { return session.Query<ReportEntity>().ToDictionary<ReportEntity, string, string>(report => report.Url, report => report.Url); } } public override bool IsValidUrl(string url) { // Check if the specified URL is valid for the current report storage. // In this example, a URL should be a string containing a numeric value that is used as a data row primary key. return true; } public override void SetData(XtraReport report, string url) { // Write a report to the storage under the specified URL. using (var session = SessionFactory.Create()) { var reportEntity = session.GetObjectByKey<ReportEntity>(url); MemoryStream ms = new MemoryStream(); report.SaveLayoutToXml(ms); reportEntity.Layout = ms.ToArray(); session.CommitChanges(); } } public override string SetNewData(XtraReport report, string defaultUrl) { // Save a report to the storage under a new URL. // The defaultUrl parameter contains the report display name specified by a user. if (CanSetData(defaultUrl)) SetData(report, defaultUrl); else using (var session = SessionFactory.Create()) { MemoryStream ms = new MemoryStream(); report.SaveLayoutToXml(ms); var reportEntity = new ReportEntity(session) { Url = defaultUrl, Layout = ms.ToArray() }; session.CommitChanges(); } return defaultUrl; } } }
Mvc_DbStorage_Sample_VB/Services/CustomReportStorageWebExtension.vb
Visual Basic
Imports System Imports System.Linq Imports System.Collections.Generic Imports DevExpress.Xpo Imports DevExpress.XtraReports.UI Imports DevExpress.XtraReports.Web.Extensions Imports System.IO Imports Mvc_DbStorage_Sample_VB.Services.DAL Public Class CustomReportStorageWebExtension Inherits ReportStorageWebExtension Public Overrides Function CanSetData(ByVal url As String) As Boolean ' Check if the URL is available in the report storage. Using session = SessionFactory.Create() Return session.GetObjectByKey(Of ReportEntity)(url) IsNot Nothing End Using End Function Public Overrides Function GetData(ByVal url As String) As Byte() ' Get the report data from the storage. Using session = SessionFactory.Create() Dim report = session.GetObjectByKey(Of ReportEntity)(url) Return report.Layout End Using End Function Public Overrides Function GetUrls() As Dictionary(Of String, String) ' Get URLs and display names for all reports available in the storage Using session = SessionFactory.Create() Return session.Query(Of ReportEntity)().ToDictionary(Function(report) report.Url, Function(report) report.Url) End Using End Function Public Overrides Function IsValidUrl(ByVal url As String) As Boolean ' Check if the specified URL is valid for the current report storage. ' In this example, a URL should be a string containing a numeric value that is used as a data row primary key. Return True End Function Public Overrides Sub SetData(ByVal report As XtraReport, ByVal url As String) ' Write a report to the storage under the specified URL. Using session = SessionFactory.Create() Dim reportEntity = session.GetObjectByKey(Of ReportEntity)(url) Dim ms As New MemoryStream() report.SaveLayoutToXml(ms) reportEntity.Layout = ms.ToArray() session.CommitChanges() End Using End Sub Public Overrides Function SetNewData(ByVal report As XtraReport, ByVal defaultUrl As String) As String ' Save a report to the storage under a new URL. ' The defaultUrl parameter contains the report display name specified by a user. If CanSetData(defaultUrl) Then SetData(report, defaultUrl) Else Using session = SessionFactory.Create() Dim ms As New MemoryStream() report.SaveLayoutToXml(ms) Dim reportEntity = New ReportEntity(session) With { .Url = defaultUrl, .Layout = ms.ToArray() } session.CommitChanges() End Using End If Return defaultUrl End Function End Class
Mvc_DbStorage_Sample/DAL/ReportEntity.cs
C#
using DevExpress.Xpo; namespace Mvc_DbStorage_Sample.Services.DAL { [DeferredDeletion(false)] public class ReportEntity : XPCustomObject { string url; string name; byte[] layout; public ReportEntity(Session session) : base(session) { } [Key] public string Url { get { return url; } set { SetPropertyValue("Url", ref url, value); } } public byte[] Layout { get { return layout; } set { SetPropertyValue("Layout", ref layout, value); } } } }
Mvc_DbStorage_Sample_VB/DAL/ReportEntity.vb
Visual Basic
Imports DevExpress.Xpo Namespace Services.DAL <DeferredDeletion(False)> Public Class ReportEntity Inherits XPCustomObject Private url_Renamed As String Private name As String Private layout_Renamed() As Byte Public Sub New(ByVal session As Session) MyBase.New(session) End Sub <Key> Public Property Url() As String Get Return url_Renamed End Get Set(ByVal value As String) SetPropertyValue("Url", url_Renamed, value) End Set End Property Public Property Layout() As Byte() Get Return layout_Renamed End Get Set(ByVal value As Byte()) SetPropertyValue("Layout", layout_Renamed, value) End Set End Property End Class End Namespace
Mvc_DbStorage_Sample/DAL/SessionFactory.cs
C#
using DevExpress.Xpo; using DevExpress.Xpo.Metadata; using System.Web.Configuration; namespace Mvc_DbStorage_Sample.Services.DAL { public static class SessionFactory { static readonly IDataLayer dataLayer; static SessionFactory() { var dictionary = new ReflectionDictionary(); dictionary.GetDataStoreSchema(typeof(SessionFactory).Assembly); var connectionString = WebConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString; var dataStore = XpoDefault.GetConnectionProvider(connectionString, DevExpress.Xpo.DB.AutoCreateOption.DatabaseAndSchema); dataLayer = new ThreadSafeDataLayer(dictionary, dataStore); } public static UnitOfWork Create() { return new UnitOfWork(dataLayer); } } }
Mvc_DbStorage_Sample_VB/DAL/SessionFactory.vb
Visual Basic
Imports DevExpress.Xpo Imports DevExpress.Xpo.Metadata Imports System.Web.Configuration Namespace Services.DAL Public Module SessionFactory Private ReadOnly dataLayer As IDataLayer Sub New() Dim dictionary = New ReflectionDictionary() dictionary.GetDataStoreSchema(GetType(SessionFactory).Assembly) Dim connectionString = WebConfigurationManager.ConnectionStrings("DefaultConnection").ConnectionString Dim dataStore = XpoDefault.GetConnectionProvider(connectionString, DevExpress.Xpo.DB.AutoCreateOption.DatabaseAndSchema) dataLayer = New ThreadSafeDataLayer(dictionary, dataStore) End Sub Public Function Create() As UnitOfWork Return New UnitOfWork(dataLayer) End Function End Module End Namespace
Mvc_DbStorage_Sample/Global.asax.cs
C#
using System; using System.Collections.Generic; using System.Linq; using System.IO; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Routing; using DevExpress.Web.Mvc; using Mvc_DbStorage_Sample.Services; namespace Mvc_DbStorage_Sample { // 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()); 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 } } }
Mvc_DbStorage_Sample_VB/Global.asax.vb
Visual Basic
' Note: For instructions on enabling IIS6 or IIS7 classic mode, ' visit http://go.microsoft.com/?LinkId=9394802 Imports System.Web.Http Imports DevExpress.Web.Mvc Imports Mvc_DbStorage_Sample_VB.Services Public Class MvcApplication Inherits System.Web.HttpApplication Sub 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()) System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol Or System.Net.SecurityProtocolType.Tls12 MVCxReportDesigner.StaticInitialize() DevExpress.XtraReports.Web.ClientControls.LoggerService.Initialize(Sub(ex, message) System.Diagnostics.Debug.WriteLine("[{0}]: Exception occurred. Message: '{1}'. Exception Details:\r\n{2}", DateTime.Now, message, ex) End Sub) AreaRegistration.RegisterAllAreas() GlobalConfiguration.Configure(AddressOf WebApiConfig.Register) FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters) RouteConfig.RegisterRoutes(RouteTable.Routes) ModelBinders.Binders.DefaultBinder = new DevExpress.Web.Mvc.DevExpressEditorsBinder() AddHandler DevExpress.Web.ASPxWebControl.CallbackError, AddressOf Application_Error End Sub Protected Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs) Dim exception As Exception = System.Web.HttpContext.Current.Server.GetLastError() 'TODO: Handle Exception End Sub End Class
Mvc_DbStorage_Sample/Models/DesignModel.cs
C#
public class DesignModel { public string Url { get; set; } public object DataSource { get; set; } }
Mvc_DbStorage_Sample_VB/Models/DesignModel.vb
Visual Basic
Public Class DesignModel Public Property Url() As String Public Property DataSource() As Object End Class
Mvc_DbStorage_Sample/Models/IndexModel.cs
C#
public class IndexModel { public ReportModel[] Reports { get; set; } public string SelectedReportUrl { get; set; } }
Mvc_DbStorage_Sample_VB/Models/IndexModel.vb
Visual Basic
Public Class IndexModel Public Property Reports() As ReportModel() Public Property SelectedReportUrl() As String End Class
Mvc_DbStorage_Sample/Models/ReportModel.cs
C#
public class ReportModel { public string Url { get; set; } }
Mvc_DbStorage_Sample_VB/Models/Reportmodel.vb
Visual Basic
Public Class ReportModel Public Property Url() As String End Class
Mvc_DbStorage_Sample/Views/Home/Designer.cshtml
Razor
@model DesignModel @{ ViewBag.Title = "Designer"; } <link rel="stylesheet" type="text/css" href="~/Content/Designer.css" /> <script type="text/javascript"> function reportDesigner_ExitDesigner(s, e) { window.location = '@Url.Action("Index")'; } </script> @{ var designer = Html.DevExpress().ReportDesigner(settings => { settings.Name = "reportDesigner"; if (Model.DataSource != null) { settings.DataSources.Add("Categories", Model.DataSource); } settings.ClientSideEvents.ExitDesigner = "reportDesigner_ExitDesigner"; }); if (Model.Url != null) { designer.BindToUrl(Model.Url).Render(); } else { designer.Bind(new XtraReport()).Render(); } }
Mvc_DbStorage_Sample_VB/Views/Home/Designer.vbhtml
Code
@ModelType Mvc_DbStorage_Sample_VB.DesignModel @Code ViewBag.Title = "Designer" End Code <link rel="stylesheet" type="text/css" href="~/Content/Designer.css" /> <script type="text/javascript"> function reportDesigner_ExitDesigner(s, e) { window.location = '@Url.Action("Index")'; } </script> @Code Dim designer = Html.DevExpress().ReportDesigner(Sub(settings) settings.Name = "reportDesigner" If Not IsNothing(Model.DataSource) Then settings.DataSources.Add("Categories", Model.DataSource) End If settings.ClientSideEvents.ExitDesigner = "reportDesigner_ExitDesigner" End Sub) If Model.Url IsNot Nothing Then designer.BindToUrl(Model.Url).Render() Else designer.Bind(New XtraReport()).Render() End If End Code
Mvc_DbStorage_Sample/Views/Home/Index.cshtml
Razor
@model IndexModel @{ ViewBag.Title = "Index"; } <link rel="stylesheet" type="text/css" href="~/Content/Index.css" /> <h2>Report Catalog</h2> @using (Html.BeginForm()) { <span>Use the form below to manage reports in the catalog.</span> @Html.DevExpress().ListBoxFor(x => x.SelectedReportUrl, settings => { settings.Name = "Url"; settings.Properties.TextField = "Url"; settings.Properties.ValueField = "Url"; settings.Properties.ValueType = typeof(string); settings.ControlStyle.CssClass = "reports"; settings.Width = 600; }).BindList(Model.Reports).GetHtml() @Html.DevExpress().Button(settings => { settings.Name = "EditReportButton"; settings.RouteValues = new { Controller = "Home", Action = "Design" }; settings.UseSubmitBehavior = true; settings.Text = "Run Designer"; settings.ControlStyle.CssClass = "buttonSeparator"; settings.Width = 100; }).GetHtml() @Html.DevExpress().Button(settings => { settings.Name = "DeleteReportButton"; settings.RouteValues = new { Controller = "Home", Action = "Delete" }; settings.UseSubmitBehavior = true; settings.Text = "Delete"; settings.Width = 100; }).GetHtml() }
Mvc_DbStorage_Sample_VB/Views/Home/Index.vbhtml
Code
@ModelType Mvc_DbStorage_Sample_VB.IndexModel @Code ViewBag.Title = "Index" End Code <link rel="stylesheet" type="text/css" href="~/Content/Index.css" /> <h2>Report Catalog</h2> @Using Html.BeginForm() ViewContext.Writer.Write("<span>Use the form below to manage reports in the catalog.</span>") @Html.DevExpress().ListBoxFor(Function(x) x.SelectedReportUrl, Sub(settings) settings.Name = "Url" settings.Properties.TextField = "Url" settings.Properties.ValueField = "Url" settings.Properties.ValueType = GetType(String) settings.ControlStyle.CssClass = "reports" settings.Width = 600 End Sub).BindList(Model.Reports).GetHtml() @Html.DevExpress().Button(Sub(settings) settings.Name = "EditReportButton" settings.RouteValues = New With {.Controller = "Home", .Action = "Design"} settings.UseSubmitBehavior = True settings.Text = "Run Designer" settings.ControlStyle.CssClass = "buttonSeparator" settings.Width = 100 End Sub).GetHtml() @Html.DevExpress().Button(Sub(settings) settings.Name = "DeleteReportButton" settings.RouteValues = New With {.Controller = "Home", .Action = "Delete"} settings.UseSubmitBehavior = True settings.Text = "Delete" settings.Width = 100 End Sub).GetHtml() End Using

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.