Skip to main content
.NET 8.0+

How to: Display Non-Persistent Objects in a Report

  • 4 minutes to read

This topic describes how to create a report based on non-persistent data. Use this approach to analyze and report data obtained from runtime calculations, stored procedures, arbitrary SQL queries, or third-party services.

Create a Report and Bind It to a Non-Persistent Data Type

  1. Declare a non-persistent class (for example, MyNonPersistentObject), and decorate it with the DomainComponent and VisibleInReports attributes.

    using DevExpress.ExpressApp.DC;
    using DevExpress.Persistent.Base;
    // ...
    [DomainComponent, VisibleInReports]
    public class MyNonPersistentObject : NonPersistentBaseObject {
        public string Name { get; set; }
    }
    
  2. Create a report for the MyNonPersistentObject data type. You can add a predefined static report in Visual Studio or create a report at runtime.

    Blazor - Specify Object Type

    Important

    Non-persistent objects do not support the ViewDataSource component – use CollectionDataSource to bind a report to data.

    At this step, the created report displays no data in the preview.

    Blazor - Empty Report

Supply the Report with Data (Initialize Non-Persistent Objects)

In .NET Applications

In .NET applications, you can use one of the following techniques to generate report data.

Use the Application Builder

In the Application Builder code, handle the NonPersistentObjectSpace.ObjectsGetting event as shown below:

File: MySolution.Blazor.Server\Startup.cs, MySolution.Win\Startup.cs

using System.ComponentModel;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Core;
// ...
// Handle the `NonPersistentObjectSpace.ObjectsGetting` event.
builder.ObjectSpaceProviders.Events.OnObjectSpaceCreated = context => {
    if (context.ObjectSpace is NonPersistentObjectSpace nonPersistentObjectSpace) {
        nonPersistentObjectSpace.ObjectsGetting += NonPersistentObjectSpace_ObjectsGetting; 
    }
};
// ...
private static void NonPersistentObjectSpace_ObjectsGetting(object sender, ObjectsGettingEventArgs e) {
    // In the event handler, populate the `e.Objects` collection 
    // with non-persistent objects of the required type.
    if (e.ObjectType == typeof(MyNonPersistentObject)) {
        BindingList<MyNonPersistentObject> objects = new BindingList<MyNonPersistentObject>();
        for (int i = 1; i < 10; i++) {
            objects.Add(new MyNonPersistentObject() { Name = string.Format("Object {0}", i) });
        }
        e.Objects = objects;
    }
}
// ...

Implement an IObjectSpaceCustomizer

  1. In the module project, implement the IObjectSpaceCustomizer service as shown below:

    File: MySolution.Module\NonPersistentObjectSpaceCustomizer.cs

    using System.ComponentModel;
    using DevExpress.ExpressApp;
    using DevExpress.ExpressApp.Core;
    
    public class NonPersistentObjectSpaceCustomizer : IObjectSpaceCustomizer {
        private readonly IObjectSpaceProviderService objectSpaceProvider;
        private readonly IObjectSpaceCustomizerService objectSpaceCustomizerService;
    
        public NonPersistentObjectSpaceCustomizer(
        IObjectSpaceProviderService objectSpaceProvider, 
        IObjectSpaceCustomizerService objectSpaceCustomizerService) {
            this.objectSpaceProvider = objectSpaceProvider;
            this.objectSpaceCustomizerService = objectSpaceCustomizerService;
        }
    
        public void OnObjectSpaceCreated(IObjectSpace objectSpace) {
            if(objectSpace is NonPersistentObjectSpace nonPersistentObjectSpace) {
                // Handle the `NonPersistentObjectSpace.ObjectsGetting` event.
                nonPersistentObjectSpace.ObjectsGetting += NonPersistentObjectSpace_ObjectsGetting;
                nonPersistentObjectSpace.ObjectByKeyGetting += NonPersistentObjectSpace_ObjectByKeyGetting;
                nonPersistentObjectSpace.Committing += NonPersistentObjectSpace_Committing;
                nonPersistentObjectSpace.PopulateAdditionalObjectSpaces(objectSpaceProvider, objectSpaceCustomizerService);
            }
        }
    
        private void NonPersistentObjectSpace_ObjectsGetting(object? sender, ObjectsGettingEventArgs e) {
            // In the event handler, populate the `e.Objects` collection 
            // with non-persistent objects of the required type.
            if (e.ObjectType == typeof(MyNonPersistentObject)) {
                BindingList<MyNonPersistentObject> objects = new BindingList<MyNonPersistentObject>();
                for (int i = 1; i < 10; i++) {
                    objects.Add(new MyNonPersistentObject() { Name = string.Format("Object {0}", i) });
                }
                e.Objects = objects;
            }
        }
        private void NonPersistentObjectSpace_ObjectByKeyGetting(object? sender, ObjectByKeyGettingEventArgs e) {
            //...
        }
        private void NonPersistentObjectSpace_Committing(object? sender, CancelEventArgs e) {
            //...
        }
    }
    
  2. Register your IObjectSpaceCustomizer service implementation in the Starup.cs file of the Blazor and WinForms applications. Use the TryAddEnumerable method to register these services:

    File: MySolution.Blazor.Server\Startup.cs, MySolution.Win\Startup.cs

    public void ConfigureServices(IServiceCollection services) {
        //...
        services.TryAddEnumerable(ServiceDescriptor.Scoped<IObjectSpaceCustomizer, 
            NonPersistentObjectSpaceCustomizer>());
        //...
    }
    

The following image demonstrates the result:

Blazor - Result

In .NET Framework Applications

  1. Open the WinApplication.cs and/or WebApplication.cs code. Ensure that the NonPersistentObjectSpaceProvider is registered in the overridden CreateDefaultObjectSpaceProvider method (in addition to the existing XPObjectSpaceProvider or EFObjectSpaceProvider). The Solution Wizard automatically adds this code but it might be missing if you used an older XAF version to create your project.

    File: MySolution.Win\WinApplication.cs, MySolution.Web\WebApplication.cs

    protected override void CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) {
        // ...
        args.ObjectSpaceProviders.Add(new NonPersistentObjectSpaceProvider(TypesInfo, null));
    }
    
  2. Subscribe to the XafApplication.ObjectSpaceCreated event, and subscribe to the NonPersistentObjectSpace.ObjectsGetting event in its handler. In the ObjectsGetting handler, check if the requested object type is a MyNonPersistentObject type and populate the e.Objects collection.

    File: MySolution.Module\Module.cs

    using System;
    using System.ComponentModel;
    using DevExpress.ExpressApp;
    // ...
    public sealed partial class MySolutionModule : ModuleBase {
        //...
        public override void Setup(XafApplication application) {
            base.Setup(application);
            application.SetupComplete += Application_SetupComplete;
        }
        private void Application_SetupComplete(object sender, EventArgs e) {
            Application.ObjectSpaceCreated += Application_ObjectSpaceCreated;
        }
        private void Application_ObjectSpaceCreated(object sender, ObjectSpaceCreatedEventArgs e) {
            var nonPersistentObjectSpace = e.ObjectSpace as NonPersistentObjectSpace;
            if(nonPersistentObjectSpace != null) {
                nonPersistentObjectSpace.ObjectsGetting += ObjectSpace_ObjectsGetting;
            }
        }
        private void ObjectSpace_ObjectsGetting(Object sender, ObjectsGettingEventArgs e) {
            if (e.ObjectType == typeof(MyNonPersistentObject)) {
                BindingList<MyNonPersistentObject> objects = new BindingList<MyNonPersistentObject>();
                for (int i = 1; i < 10; i++) {
                    objects.Add(new MyNonPersistentObject() { Name = string.Format("Object {0}", i) });
                }
                e.Objects = objects;
            }
        }
    }
    

The following image demonstrates the result.

WinForms - Result