Non-Persistent Objects
- 13 minutes to read
A non-persistent class is a type of business class. XAF generates a UI for this class but does not bind it to an application’s database table. You can use this class to display a List or Detail View with temporary data generated in code or loaded from storage. You can also use it to display an empty View (dialog) and process the user’s input.
Important Notes
- Do not inherit a non-persistent class from an XPO persistent class (including Base Persistent Classes). Otherwise, the SessionMixingException or ObjectDisposedException can occur in your application because the
NonPersistentObjectSpace
andNonPersistentObjectSpaceProvider
are designed to work with a Session. - Do not use the NonPersistentAttribute to make a class non-persistent. For this purpose, apply the DomainComponentAttribute to a POCO class as shown in the Basic Non-Persistent Class Implementation section.
- The Type permissions list does not include non-persistent object types and everyone can access them. Refer to the Restrict Non-Persistent Types Access section to learn how to include these types in the Type permissions list.
Basic Non-Persistent Class Implementation
In the module project, declare a regular class and add the DomainComponentAttribute to implement a non-persistent class. You can inherit your class from the NonPersistentBaseObject
, NonPersistentLiteObject
, NonPersistentObjectImpl
, or NonPersistentEntityObject
class. All of these classes implement the INotifyPropertyChanged and IXafEntityObject interfaces and support Custom Fields. The NonPersistentBaseObject
and NonPersistentLiteObject
classes implement the read-only Oid
key property of the Guid
type. This key is initialized with a random value in a constructor. If you want to use a key property of a different type or a writable property (for example, for serialization), inherit from NonPersistentObjectImpl
or NonPersistentEntityObject
and implement the key property. The NonPersistentBaseObject
and NonPersistentObjectImpl
classes implement IObjectSpaceLink.
The following table lists the base non-persistent classes:
Class | Implements IObjectSpaceLink | Contains the Auto-Generated Primary Key Property |
---|---|---|
NonPersistentBaseObject |
Yes | Yes, Oid (Guid type) |
NonPersistentLiteObject |
No | Yes, Oid (Guid type) |
NonPersistentObjectImpl |
Yes | No |
NonPersistentEntityObject |
No | No |
Note
You cannot share non-persistent objects that implement IObjectSpaceLink between different Object Spaces. For example, an exception is thrown when you open a Detail View for an object selected in a List View. To address this exception, subscribe to the NonPersistentObjectSpace.ObjectGetting event, create a new object instance, and copy property values from the source object.
The following code example shows how to implement a non-persistent class:
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.DC;
// ...
[DomainComponent]
public class MyNonPersistentObject : NonPersistentBaseObject {
private String name;
private String description;
public override void OnSaving() {
base.OnSaving();
// ...
}
public String Name {
get { return name; }
set { SetPropertyValue(ref name, value); }
}
public String Description {
get { return description; }
set { SetPropertyValue(ref description, value); }
}
}
XAF automatically registers the class with this attribute in the Types Info Subsystem and adds it to the Application Model. Rebuild the solution and open the Model Editor to ensure that XAF added the class to the BOModel node and created the corresponding List and Detail Views.
XAF can display this MyNonPersistentObject
Detail View in a PopupWindowShowAction popup dialog. See the following help topic for details: Add an Action that Displays a Pop-Up Window.
You can also use the ActionAttribute to show a non-persistent object’s popup. For this purpose, apply this attribute to a business class method that takes a non-persistent type parameter.
Examples:
- How to: Create an Action Using the Action Attribute
- How to: Display a List of Non-Persistent Objects in a Popup Dialog
- How to: Display and Edit Simple Type Values in a Lookup Property Editor
Non-Persistent Object Space
NonPersistentObjectSpace is an Object Space used to manage non-persistent object instances in your application. You can create, read, or update non-persistent object instances from code if the XafApplication object supports this Object Space type.
.NET Core Apps
In .NET applications (applications with the Application Builder), you need to use the Application Builder’s ObjectSpaceProviders property to register Object Space Providers. Refer to the following help topic for more information on how to do this: Integrate Application Builders into Existing Applications.
File: MySolution.Blazor.Server/Startup.cs, MySolution.Win/Startup.cs, MySolution.WebApi/Startup.cs
// ...
builder.ObjectSpaceProviders
// ...
.AddNonPersistent();
// ...
This technique also improves performance of your application because the application instance is not created in this case.
If you use another technique (for example, the XafApplication.CreateDefaultObjectSpaceProvider
method or the XafApplication.CreateCustomObjectSpaceProvider
event mentioned below with Application Builder-based applications), this technique may conflict with IObjectSpaceFactory and IObjectSpaceProviderFactory service usage.
Apps Without the Application Builder
To support a Non-Persistent Object Space in applications without the Application Builder, add the following code to the overridden CreateDefaultObjectSpaceProvider
method in the WinApplication.cs, and/or WebApplication.cs file (in addition to the existing XPObjectSpaceProvider or EFObjectSpaceProvider
registration).
protected override void CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) {
// ...
args.ObjectSpaceProviders.Add(new NonPersistentObjectSpaceProvider(TypesInfo, null));
}
Note that the Solution Wizard adds the code snippet above automatically.
Common Information
XAF uses the first registered Object Space Provider for the following purposes:
- To get XafApplication.ObjectSpaceProvider and XafApplication.ConnectionString property values.
- To pass this Provider as the CustomCheckCompatibilityEventArgs
ObjectSpaceProvider
argument. - To update an application.
Ensure that NonPersistentObjectSpaceProvider
is not the first registered Provider in your application.
Important
NonPersistentObjectSpace
cannot handle persistent objects. To process these objects, create an additional Object Space for the persistent object type and add it to the NonPersistentObjectSpace.AdditionalObjectSpaces collection. For more information, refer to the following help topic: How to: Show Persistent Objects in a Non-Persistent Object’s View.
Also, note that you may need to refresh and dispose of Object Spaces from this collection and the parent non-persistent Object Space simultaneously. To do this automatically, use the following properties and fields:
Use the CreateObjectSpace(Type) method to create an Object Space and manage non-persistent objects in your code (for example, in the Controller‘s code):
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Actions;
// ...
public class ShowDetailViewController : ViewController<ListView> {
// ...
void showDetailViewAction_CustomizePopupWindowParams(
object sender, CustomizePopupWindowParamsEventArgs e) {
IObjectSpace newObjectSpace = Application.CreateObjectSpace(typeof(NonPersistentObject));
// ...
}
}
When you create a NonPersistentObjectSpace
instance in a Controller (for example, to show a custom dialog), you can customize it in the Controller’s code.
To specify default settings for all NonPersistentObjectSpaces in your application, use one of the following techniques depending on your target platform:
In .NET applications, handle the
OnObjectSpaceCreated
event in the application builder code within the application’sStartup.cs
file and subscribe to the required NonPersistentObjectSpace events:File: MySolution.Blazor.Server/Startup.cs, MySolution.Win/Startup.cs, MySolution.WebApi/Startup.cs
using DevExpress.ExpressApp; // ... builder.ObjectSpaceProviders.Events.OnObjectSpaceCreated = context => { NonPersistentObjectSpace nonPersistentObjectSpace = context.ObjectSpace as NonPersistentObjectSpace; if(nonPersistentObjectSpace != null) { // Subscribe to NonPersistentObjectSpace events and customize their properties } }; // ...
In .NET Framework applications, handle the XafApplication.ObjectSpaceCreated event and subscribe to the required NonPersistentObjectSpace events. You can do this in a Module after application setup is complete:
File: MySolution.Module/Module.cs
using System; 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) { NonPersistentObjectSpace nonPersistentObjectSpace = e.ObjectSpace as NonPersistentObjectSpace; if(nonPersistentObjectSpace != null) { // Subscribe to NonPersistentObjectSpace events and customize their properties } } }
Note
We do not recommend that you subscribe to the XafApplication.ObjectSpaceCreated event from a View or Window Controller. This may lead to multiple event invocations, because Controllers are created for each Frame. For example, a Window Controller that targets the main window can be instantiated and activated multiple times when you use it in a WinForms application with UIType = TabbedMDI mode or a Web Forms application with EnableMultipleBrowserTabsSupport = true
.
To supply data to non-persistent objects, handle the following NonPersistentObjectSpace
events raised when the Object Space loads non-persistent objects:
- NonPersistentObjectSpace.ObjectGetting
- NonPersistentObjectSpace.ObjectsGetting
- NonPersistentObjectSpace.ObjectByKeyGetting
Since these events are raised to request any non-persistent object (declared in custom or built-in Modules), check the object type passed in event parameters before modifying the events’ output parameters.
You can use the New, Delete, and Save Actions to manipulate non-persistent objects. The NonPersistentObjectSpace.ModifiedObjects collection contains all modified objects. Newly created and deleted objects are added to this collection automatically. To add an existing object to this collection, use the IObjectSpace.SetModified(Object) method. If an object implements the INotifyPropertyChanged interface, you can set AutoSetModifiedOnObjectChange or AutoSetModifiedOnObjectChangeByDefault to true
to add this object to the ModifiedObjects collection automatically when its property is changed.
Examples
- How to: Display a Non-Persistent Object’s List View from the Navigation
- How to: Display a Non-Persistent Object’s Detail View from the Navigation
- How to: Perform CRUD Operations with Non-Persistent Objects
- How to: Display Non-Persistent Objects in a Report
Key Property
Key properties are required in ASP.NET Web Forms and ASP.NET Core Blazor applications. Use the KeyAttribute to declare a key property.
The following code snippet demonstrates how to declare a key property in a non-persistent class. The BrowsableAttribute hides this property from the UI.
[DomainComponent]
public class NonPersistentObject {
[Browsable(false)]
[DevExpress.ExpressApp.Data.Key]
public int Oid { get; set; }
// ...
}
Important
Use the Key
attribute from the DevExpress.ExpressApp.Data
namespace only (not from the System.ComponentModel.DataAnnotations or DevExpress.Xpo namespace).
You can also inherit the non-persistent class from NonPersistentBaseObject
or NonPersistentLiteObject
that implements the read-only Oid
key property of the Guid
type. This key is initialized with a random value in a constructor.
You can allow users to open a specific non-persistent object instance from the Navigation:
Add an item to the navigation. Set its IModelNavigationItem.View property to the identifier of the non-persistent object’s DetailView and the IModelNavigationItem.ObjectKey property to an arbitrary integer value.
Subscribe to the NonPersistentObjectSpace.ObjectByKeyGetting event handler. Find an object instance whose type matches the event’s
e.ObjectType
parameter and whose key value coincides with thee.Key
parameter. Pass this object to the event’se.Object
parameter.
Example: How to: Display a Non-Persistent Object’s Detail View from the Navigation
Filter and Sort Non-Persistent Objects
You can use the DevExpress.ExpressApp.DynamicCollection
to filter and sort a collection of non-persistent objects. Follow the steps below to do this.
- Handle the NonPersistentObjectSpace.ObjectsGetting event.
- In this event handler, create a new
DynamicCollection
and subscribe to itsFetchObjects
event. This event is raised after sort or filter parameters are changed or the collection is reloaded. - Pass a list of non-persistent objects to the Objects event argument. You can filter and sort this list based on event arguments; to do this automatically, set the
ShapeData
argument totrue
. - Pass the
DynamicCollection
instance to theNonPersistentObjectSpace.ObjectsGetting
event’sObjects
argument.
The following code demonstrates how you can implement it in a .NET application:
File: MySolution.Blazor.Server/Startup.cs, MySolution.Win/Startup.cs, MySolution.WebApi/Startup.cs
using System;
using System.Collections.Generic;
using DevExpress.ExpressApp;
// ...
builder.ObjectSpaceProviders.Events.OnObjectSpaceCreated = context => {
NonPersistentObjectSpace nonPersistentObjectSpace = context.ObjectSpace as NonPersistentObjectSpace;
if (nonPersistentObjectSpace != null) {
nonPersistentObjectSpace.ObjectsGetting += NonPersistentObjectSpace_ObjectsGetting;
}
};
// ...
private void NonPersistentObjectSpace_ObjectsGetting(object sender, ObjectsGettingEventArgs e) {
if (e.ObjectType == typeof(Contact)) {
DynamicCollection collection = new DynamicCollection((IObjectSpace)sender, e.ObjectType, e.Criteria, e.Sorting, e.InTransaction);
collection.FetchObjects += DynamicCollection_FetchObjects;
e.Objects = collection;
}
}
private void DynamicCollection_FetchObjects(object sender, FetchObjectsEventArgs e) {
if (e.ObjectType == typeof(Contact)) {
e.Objects = contacts; // Your collection of non-persistent objects.
e.ShapeData = true; // Set to true if the supplied collection is not already filtered and sorted.
}
}
The following code example demonstrates how to implement filter and sort functionality in a .NET Framework application:
File: MySolution.Module/Module.cs
using System;
using System.Collections.Generic;
using DevExpress.ExpressApp;
// ...
public sealed partial class NonPersistentObjectsDemoModule : ModuleBase {
private List<Contact> contacts;
// ...
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) {
NonPersistentObjectSpace nonPersistentObjectSpace = e.ObjectSpace as NonPersistentObjectSpace;
if (nonPersistentObjectSpace != null) {
nonPersistentObjectSpace.ObjectsGetting += ObjectSpace_ObjectsGetting;
}
}
private void ObjectSpace_ObjectsGetting(object sender, ObjectsGettingEventArgs e) {
if (e.ObjectType == typeof(Contact)) {
DynamicCollection collection = new DynamicCollection((IObjectSpace)sender, e.ObjectType, e.Criteria, e.Sorting, e.InTransaction);
collection.FetchObjects += DynamicCollection_FetchObjects;
e.Objects = collection;
}
}
private void DynamicCollection_FetchObjects(object sender, FetchObjectsEventArgs e) {
if (e.ObjectType == typeof(Contact)) {
e.Objects = contacts; // Your collection of non-persistent objects.
e.ShapeData = true; // Set to true if the supplied collection is not already filtered and sorted.
}
}
}
To see the full example, refer to the following GitHub repository: How to filter and sort Non-Persistent Objects.
If users can create new objects in Lookup List Views, cache the collection that the DynamicCollection.FetchObjects
event returns. Otherwise, newly added objects disappear from the Lookup List View after it is reopened. The following GitHub repository shows how to do this: How to edit Non-Persistent Objects nested in a Persistent Object.
INotifyPropertyChanged Support
All of the base non-persistent classes listed above implement INotifyPropertyChanged. To use this interface’s functionality, declare property setters as follows:
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.DC;
// ...
[DomainComponent]
public class MyNonPersistentObject : NonPersistentBaseObject {
//...
private String description;
public String Description {
get { return description; }
set { SetPropertyValue(ref description, value); }
}
}
If you do not use base classes, implement the INotifyPropertyChanged interface in your non-persistent class to use XAF features that track and handle property value changes (for example, Conditional Appearance). Call the OnPropertyChanged
method from the property setter to trigger the PropertyChanged event. The Object Space handles this event internally. Then the PropertyChanged
event triggers the IObjectSpace.ObjectChanged event.
Example: How to: Perform CRUD Operations with Non-Persistent Objects | The Importance of Property Change Notifications for Automatic UI Updates
IXafEntityObject and IObjectSpaceLink Support
Implement the IXafEntityObject interface to add custom business logic to the non-persistent class code. This interface declares the OnCreated
and OnSaved
methods that are called when the Object Space creates or saves an object. You can also implement the IObjectSpaceLink interface and use the Object Space that the IObjectSpaceLink.ObjectSpace property returns. This allows you to query other objects in the same and additional Object Spaces.
using DevExpress.ExpressApp;
// ...
[DomainComponent]
public class MyNonPersistentObject : IXafEntityObject, IObjectSpaceLink{
// ...
void IXafEntityObject.OnCreated() {
// Place the entity initialization code here.
// You can use Object Space methods, for example, to initialize reference properties:
// this.Address = objectSpace.CreateObject<Address>();
}
void IXafEntityObject.OnLoaded() {
// Place the code that is executed each time the entity is loaded here. Explicitly call this method in your code if needed.
}
void IXafEntityObject.OnSaving() {
// Place the code that is executed each time the entity is saved here.
}
IObjectSpace IObjectSpaceLink.ObjectSpace {
get { return objectSpace; }
set { objectSpace = value; }
}
}
When a non-persistent object contains a reference to one persistent object and another persistent object references this non-persistent object, the IObjectSpaceLink.ObjectSpace property may return an Additional Object Space instead of its owner Object Space. To get the owner Object Space, use the IObjectSpace.Owner property (see the code below):
IObjectSpace ownerObjectSpace = (ObjectSpace.Owner as IObjectSpace) ?? ObjectSpace;
Example: How to: Perform CRUD Operations with Non-Persistent Objects
Non-Persistent Object Template
You can use a Visual Studio template to create a non-persistent class. Open the Template Gallery, switch to the XAF category, and choose the XAF Business Object | Non-Persistent Object item. The added class can be used in various complex scenarios listed in this topic.
This non-persistent class includes the IXafEntityObject
, IObjectSpaceLink
, and INotifyPropertyChanged
interface implementations, and an integer type key property.