KB Article T339643
Visible to All Users

How to access the values stored in HttpContext/Session while working with the ASP.NET HTML5 Document Viewer and End-User Report Designer controls

Description:
A separate HTTP handler module is used by our End-User Report Designer for ASP.NET to open/save a report to custom report storage (ReportStorageWebExtension). So, the HttpContext.Session and HttpContext.User properties are not available when your custom report storage methods are executed.
Besides, the ASP.NET HTML5 Document Viewer control generates a previewed report's documents in a separate thread. So, HttpContext is not available while the report document is being created: the HttpContext.Current method will return the NULL value in the report events' code.

This article answers the following questions:

  1. How to access the values that are stored in HttpContext/Session in a ReportStorageWebExtension class descendant's code when the report is opened/saved in the End-User Report Designer for ASP.NET control?
  2. How to access the values that are stored in HttpContext/Session in the report's code behind when the report is being previewed in the HTML5 Document Viewer  control?
  3. How to access the values that are stored in HttpContext/Session in the data class code when the report is bound to an ObjectDataSource and being previewed in the HTML5 Document Viewer  control?

See the T341232: How to access the values stored in HttpContext/Session while working with the ASP.NET HTML5 Document Viewer and End-User Report Designer controls code example that demonstrates the approaches described in this article.

Answer:
1. How to access the values that are stored in HttpContext/Session in a ReportStorageWebExtension class descendant's code when the report is opened/saved in the End-User Report Designer for ASP.NET control?
Starting with version 15.2.5, the HTTP handler module that works with the designer's report storage extension provides a built-in capability to accept the Session state when a request is handled.
To enable the Session state support, set the ReportDesignerBootstrapper.SessionState property to the "SessionStateBehavior.Required" value when your application is started. To do that, add the following code to your application global class' (Global.asax) code behind:

C#
public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { DevExpress.XtraReports.Web.ReportDesigner.Native.ReportDesignerBootstrapper.SessionState = SessionStateBehavior.Required; DevExpress.XtraReports.Web.QueryBuilder.Native.QueryBuilderBootstrapper.SessionState = System.Web.SessionState.SessionStateBehavior.Required; ... } ... }

Then register the designer's HTTP handler (DXXRD.axd) in the application's configuration file (Web.config) (add this handler registration line to the httpHandlers/handlers collections after the ASPxHttpHandlerModule (DX.ashx) registration) :

XML
<system.web> ... <httpHandlers> ... <add type="DevExpress.Web.ASPxHttpHandlerModule, DevExpress.Web.v15.2, Version=15.2.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" verb="GET,POST" path="DXXRD.axd" validate="false" /> </httpHandlers> </system.web> <system.webServer> ... <handlers> ... <add type="DevExpress.Web.ASPxHttpHandlerModule, DevExpress.Web.v15.2, Version=15.2.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" verb="GET,POST" path="DXXRD.axd" name="WebReportDesignerHandler" preCondition="integratedMode" /> </handlers> </system.webServer>

After doing that, the HttpContext.Session and HttpContext.User properties will be available in your custom report storage extension's code.

2. How to access the values that are stored in HttpContext/Session in the report's code behind when the report is being previewed in the HTML5 Document Viewer  control?
As the HTML5 Document Viewer control generates a previewed report's documents in a separate thread, it is not possible to access the current request data when the report document is being generated. That's because the HttpContext object is being cleared after the request is handled by the web server, so it is not possible to access this cleared object's data from the background thread that is used for the report generation.
The only way to workaround this behavior is to pass the values from your  HttpContext/Session to your report before report creation is started. Use the report parameters for this purpose. Set these parameter's Visible property to false to hide them from the viewer's parameters panel. Then, specify the parameter values by using the values from the HttpContext/Session before opening your report in a viewer. For example, use the following code in your ASP.NET WebForms page's event handler:

C#
XtraReport report = new MyReportClassName(); report.Parameters["MyReportParameterName"].Value = Session["MySessionFieldName"]; ... ASPxWebDocumentViewer1.OpenReport(report);

Then, use this parameter's value in your report event handlers code or bind your report fields to this parameter value.

3. How to access the values that are stored in HttpContext/Session in the data class code when the report is bound to an ObjectDataSource and being previewed in the HTML5 Document Viewer  control?
As was described in the previous answer, it is not possible to access the current request data when the report document is being generated. So, the only way to pass the values from your  HttpContext/Session to a data class used by the ObjectDataSource control is to use the report parameters.
Use the approach described in the previous answer to add the parameters to your report and pass the values from the HttpContext/Session to these parameters. Then, add parameters to your data class constructor or to a method that is used to select data. After that, add corresponding parameters to the ObjectDataSource.Constructor.Parameters (in case parameters are added to your data class constructor)  or ObjectDataSource.Parameters (in case parameters are added to a method that is used to select data)  collection.
After adding the data source's parameters, it is necessary to map them to your report parameters. To do that, run the Parameters collection editor from the property grid, enable the check box in the Expression column and select your report's parameter in the Value column's drop-down. See the How to: Bind a Report to an Object Data Source > Retrieve the Actual Data help topic for more details.

Show previous comments (5)
Vasily (DevExpress Support) 5 years ago

    Hi Benjamin,

    Thank you for sharing your solution with us. Yes, this solution is absolutely correct for ASP.NET Core projects that use DevExpress components of version 19.1.5+. Earlier versions of our ASP.NET Core reporting components do not support the dependency injection for the report storage.

    H H
    HRA Solutions Ltd 3 years ago

      I came across this topic while I was attempting to achieve a similar functional requirement - our Report Designer managed additional state (in our case, a "tenant" who owned the report design) which needed to be passed to the server during save.

      I discussed a similar concern here, which was geared more at the GetUrls method of the ReportStorageWebExtension, where I came up with a different solution (injecting custom header information into every DevExpress HTTP request)
      https://supportcenter.devexpress.com/ticket/details/t348757

      This topic was originally raised about 5 years ago here:
      https://supportcenter.devexpress.com/ticket/details/t440064

      And it has been superseded by this current topic you are reading now - which is not really the same - as it's talking about using server session state to achieve these needs.
      Unfortunately, introducing statefulness to an application server is a heavy-handed solution, and massive architectural change, which comes with some undesirable side-affects, impacting the entire application.

      I have devised a better solution, and I'm surprised the DX support guys haven's suggested this.
      (I tried and failed to get this solution to work the first time around, hence my alternate solutions, but I have just recently gotten it to work, so here it is).

      XtraReport has an Extensions property, which is a key value dictionary, allowing users to store and retrieve any values they like.
      Because the ReportStorageWebExtension.SetData and ReportStorageWebExtension.SetNewData methods both receive the XtraReport, you technically have the ability to pass numerous arguments, serialized as strings.

      Here is an example of how to achieve this, in high-level Angular TS and C# code.

      On the client, register a handler for 'ReportSaving', and use it to inject your custom arguments - technically because they are persisted in the report state for the duration of the designer session - you could inject them at any point, it doesn't have to be 'last minute' like I've done for demonstration purposes here.

      HTML
      <dx-report-designer #designer height="1024px"> <dxrd-callbacks (ReportSaving)="OnReportSaving($event.args.Report, $event.args.Url, $event.component)"> </dxrd-callbacks> </dx-report-designer>
      TypeScript
      /// Helper method to add custom parameters to the report's extensions dictionary setExtensionValue(reportViewModel: ReportViewModel, key: string, value: string) { var existing = ko.utils.arrayFirst(reportViewModel.extensions(), (x) => x.key() == key); if (existing) reportViewModel.extensions.remove(existing); var newDataSerializer = new ExtensionModel({}); newDataSerializer.key = ko.observable(key); newDataSerializer.value = ko.observable(value); reportViewModel.extensions.push(newDataSerializer); } OnReportSaving(reportViewModel: ReportViewModel, url: string, component: DxReportDesignerComponent) { this.setExtensionValue(reportViewModel, 'MyCustomArgument', 'AnythingYouLike'); }
      C#
      public class CustomReportStorageWebExtension : ReportStorageWebExtension { /// <summary> /// Helper to extract a mandatory value from an XtraReport Extensions dictionary /// </summary> private T GetExtensionValue<T>(XtraReport report, string key) { string stringValue = null; if (report.Extensions.TryGetValue(key, out stringValue)) { var converter = TypeDescriptor.GetConverter(typeof(T)); return (T)converter.ConvertFromString(stringValue); } return default(T); } /// <summary> /// Helper to extract an optional value from an XtraReport Extensions dictionary /// </summary> private Nullable<T> GetNullableExtensionValue<T>(XtraReport report, string key) where T : struct { if (report.Extensions.ContainsKey(key)) return new Nullable<T>(GetExtensionValue<T>(report, key)); return default(Nullable<T>); } public override void SetData(XtraReport report, string url) { // grab your custom argument var myCustomArgument = GetNullableExtensionValue<string>(report, "MyCustomArgument"); // ...do whatever custom logic you like with it... // clear the extensions to ensure custom argument is not persisted (unless you want it to be?) report.Extensions.Clear(); SaveToSql(report); } }

      Remember that your custom extensions are embedded in the report, so they will be persisted on the server unless you remove them from the XtraReport.Extensions dictionary before sending them off to disk/sql etc.

      Happy coding!

      Vasily (DevExpress Support) 3 years ago

        Hello,

        Thank you for sharing your solution with us. However, I would like to warn you that the Extensions property is intended to serialize custom objects (e.g. services, data types, etc.) along with the report. So, the report tries to load object by using the values that are stored in this property. Thus, using this collection for storing key-value pairs may lead to unexpected behavior or even exceptions, as this collection was not intended to be used in this way.

        At the same time, you can use other properties for embedding this information into a report. For example you can use the report's Tag property or even its parameters for this purpose. But I do not recommend you to store any secure data (e.g. the tenant ID) in the report. This is because the report is passed to client and back to the sever, so the attacker may get access to this information and change it.

        Finally, I would like to mention that in ASP.NET Core applications all reporting services support dependency injection. So, you can inject desired services to the reporting services what will help you to get some user-related data (e.g. the tenant ID).

        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.