xaf-nonpersistent
XAF: NonPersistent Objects
What Are NonPersistent Objects
NonPersistent objects are business classes for which XAF generates full UI (List Views, Detail Views, Actions) but which are not mapped to any database table. They hold data loaded from external sources, computed at runtime, or used as temporary input forms.
- Decorated with
[DomainComponent]attribute (NOT[NonPersistent]from XPO — different meaning!) - Do NOT inherit from XPO persistent base classes (causes
SessionMixingException) - Managed by
NonPersistentObjectSpace - Must register
.AddNonPersistent()in app builder
Use Cases
- Display data from external REST APIs / stored procedures
- Intermediate input forms (collect parameters before executing an action)
- Computed aggregates / dashboards with no DB binding
- PopupWindowShowAction dialogs for user input
- Settings/configuration screens without persistence
Base Classes
| Class | IObjectSpaceLink | Auto Key | Notes |
|---|---|---|---|
NonPersistentBaseObject |
Yes | Yes (Guid Oid) | Recommended default |
NonPersistentLiteObject |
No | Yes | Lightweight |
NonPersistentObjectImpl |
Yes | No | Manual key |
Basic NonPersistent Object
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.DC;
[DomainComponent]
public class ContactSummary : NonPersistentBaseObject {
private string name;
private string email;
private int orderCount;
public string Name {
get => name;
set => SetPropertyValue(ref name, value);
}
public string Email {
get => email;
set => SetPropertyValue(ref email, value);
}
public int OrderCount {
get => orderCount;
set => SetPropertyValue(ref orderCount, value);
}
}
SetPropertyValue (from NonPersistentBaseObject) triggers INotifyPropertyChanged automatically.
For Blazor, always declare an explicit key:
[Browsable(false)]
[DevExpress.ExpressApp.Data.Key]
public Guid Oid { get; set; } = Guid.NewGuid();
Registration
// Startup.cs / Program.cs — MUST be after persistent providers
builder.ObjectSpaceProviders
.AddSecuredEFCoreObjectSpaceProvider<MyDbContext>(...) // persistent first
.AddNonPersistent(); // non-persistent last
The type must be in the module's ExportedTypes or declared via [DomainComponent].
ObjectsGetting Event — Providing Data for List Views
Primary pattern for populating non-persistent collections:
public class ContactSummaryController : WindowController {
protected override void OnActivated() {
base.OnActivated();
Application.ObjectSpaceCreated += Application_ObjectSpaceCreated;
}
protected override void OnDeactivated() {
Application.ObjectSpaceCreated -= Application_ObjectSpaceCreated;
base.OnDeactivated();
}
private void Application_ObjectSpaceCreated(object sender, ObjectSpaceCreatedEventArgs e) {
if (e.ObjectSpace is NonPersistentObjectSpace npos) {
npos.ObjectsGetting += NonPersistentObjectSpace_ObjectsGetting;
}
}
private void NonPersistentObjectSpace_ObjectsGetting(object sender, ObjectsGettingEventArgs e) {
if (e.ObjectType != typeof(ContactSummary)) return;
var npos = (NonPersistentObjectSpace)sender;
var items = LoadDataFromApi(); // IList<ContactSummary>
var collection = new DynamicCollection(npos, e.ObjectType, e.Criteria, e.Sorting, e.InTransaction);
collection.FetchObjects += (s, args) => {
args.Objects = items;
args.ShapeData = true; // XAF applies criteria/sorting to the list
};
e.Objects = collection;
}
private IList<ContactSummary> LoadDataFromApi() {
return new List<ContactSummary> {
new ContactSummary { Name = "Alice", Email = "alice@example.com", OrderCount = 5 },
new ContactSummary { Name = "Bob", Email = "bob@example.com", OrderCount = 2 }
};
}
}
ShapeData = true automatically applies e.Criteria and e.Sorting to your list.
ObjectByKeyGetting — Single Object Fetch
Used when XAF needs to retrieve a single object by key (e.g., navigation to Detail View):
npos.ObjectByKeyGetting += (sender, e) => {
if (e.ObjectType != typeof(ContactSummary)) return;
var key = (Guid)e.Key;
e.Object = FetchSingleContact(key);
};
ObjectDeleting / ObjectsDeleting
npos.ObjectDeleting += (sender, e) => {
if (e.Object is ContactSummary summary)
ExternalCache.Remove(summary.Oid);
};
npos.ObjectsDeleting += (sender, e) => {
foreach (ContactSummary obj in e.Objects.OfType<ContactSummary>())
ExternalCache.Remove(obj.Oid);
};
Using with PopupWindowShowAction
Classic pattern: show non-persistent form as popup to collect user input, then act on result:
public class SendEmailController : ObjectViewController<ListView, Contact> {
private PopupWindowShowAction sendEmailAction;
public SendEmailController() {
sendEmailAction = new PopupWindowShowAction(this, "SendEmail", "Edit") {
Caption = "Send Email"
};
sendEmailAction.CustomizePopupWindowParams += Action_CustomizePopupWindowParams;
sendEmailAction.Execute += Action_Execute;
}
private void Action_CustomizePopupWindowParams(
object sender, CustomizePopupWindowParamsEventArgs e) {
var os = Application.CreateObjectSpace(typeof(EmailParams));
var emailParams = os.CreateObject<EmailParams>();
// Pre-fill from current selection
emailParams.To = View.SelectedObjects.OfType<Contact>()
.Select(c => c.Email).FirstOrDefault();
// Suppress "save changes" dialog
((NonPersistentObjectSpace)os).RemoveFromModifiedObjects(emailParams);
e.View = Application.CreateDetailView(os, emailParams);
}
private void Action_Execute(object sender, PopupWindowShowActionExecuteEventArgs e) {
var emailParams = (EmailParams)e.PopupWindowViewCurrentObject;
EmailService.Send(emailParams.To, emailParams.Subject, emailParams.Body);
}
}
[DomainComponent]
public class EmailParams : NonPersistentBaseObject {
private string to, subject, body;
public string To { get => to; set => SetPropertyValue(ref to, value); }
public string Subject { get => subject; set => SetPropertyValue(ref subject, value); }
[FieldSize(FieldSizeAttribute.Unlimited)]
public string Body { get => body; set => SetPropertyValue(ref body, value); }
}
Relations with Persistent Objects
[DomainComponent]
public class OrderReport : NonPersistentBaseObject {
// Reference to persistent Customer — load via nested IObjectSpace
public Customer Customer { get; set; }
}
// In ObjectsGetting:
var persistentOs = Application.CreateObjectSpace(typeof(Customer));
var customer = persistentOs.GetObjectByKey<Customer>(customerId);
// assign to non-persistent object's property
Refreshing NonPersistent Data
// From a controller action — fires ObjectsGetting again
View.ObjectSpace.Refresh();
// Or via built-in Refresh action:
Frame.GetController<RefreshController>().RefreshAction.DoExecute();
Suppressing "Save Changes" Dialog
var os = Application.CreateObjectSpace(typeof(EmailParams));
var obj = os.CreateObject<EmailParams>();
// ... populate obj ...
((NonPersistentObjectSpace)os).RemoveFromModifiedObjects(obj);
e.View = Application.CreateDetailView(os, obj);
Common Pitfalls
| Pitfall | Solution |
|---|---|
Inheriting XPO base class + [DomainComponent] |
Use only [DomainComponent] on a POCO |
Missing explicit [Key] in Blazor |
Blazor requires [DevExpress.ExpressApp.Data.Key] property |
| NonPersistent as FIRST object space provider | Always register after persistent providers |
ObjectsGetting not firing |
Check AddNonPersistent() is registered and type is in ExportedTypes |
| Unexpected save dialog | Call RemoveFromModifiedObjects() after programmatic creation |
| Collections of persistent objects mixing object spaces | Load them via nested IObjectSpace from the parent |
Source Links
- NonPersistent Objects: https://docs.devexpress.com/eXpressAppFramework/116516/business-model-design-orm/non-persistent-objects
- NonPersistent in XAF UI: https://docs.devexpress.com/eXpressAppFramework/113471/business-model-design-orm/non-persistent-objects/non-persistent-objects-in-the-xaf-ui
- NonPersistentObjectSpace API: https://docs.devexpress.com/eXpressAppFramework/DevExpress.ExpressApp.NonPersistentObjectSpace
- DynamicCollection: https://docs.devexpress.com/eXpressAppFramework/DevExpress.ExpressApp.DynamicCollection
More from kashiash/xaf-skills
xaf
DevExpress XAF (eXpressApp Framework) master index. Use this skill first when working with any XAF topic to find the right sub-skill. Covers Blazor and WinForms, EF Core and XPO, versions v24.2 and v25.1. Sub-skills: xaf-xpo-models, xaf-ef-models, xaf-controllers, xaf-editors, xaf-custom-editors, xaf-nonpersistent, xaf-security, xaf-multi-tenant, xaf-web-api, xaf-validation, xaf-reports, xaf-dashboards, xaf-office, xaf-blazor-ui, xaf-winforms-ui, xaf-conditional-appearance, xaf-deployment, xaf-memory-leaks.
13xaf-winforms-ui
XAF WinForms UI platform - WinApplication setup, Ribbon vs Standard toolbar, WinForms-specific editors (XtraGrid, DevExpress controls), Detail View layout customization via Layout Manager, custom WinForms controls embedded in XAF views, background workers for thread-safe UI updates, splash screen customization, WinForms navigation (NavigationFrame), printing/preview in WinForms, ClickOnce/MSI deployment. Use when building or customizing XAF WinForms applications.
10xaf-blazor-ui
XAF Blazor UI platform - BlazorApplication setup in Program.cs, AddXaf/AddXafBlazor services, InvokeAsync thread safety (critical for Blazor Server), async controller actions pattern, Blazor-specific editors, embedding custom Razor components as ViewItems using IComponentContentHolder, JavaScript interop via IJSRuntime, Detail View layout customization (tabs/groups), programmatic navigation, error handling with UserFriendlyException, SignalR configuration. Use when building or customizing XAF Blazor Server applications.
10xaf-office
XAF Office/Document Management Modules - FileAttachmentsModule with IFileData/FileAttachment patterns (XPO and EF Core), SpreadsheetModule for Excel editing with ISpreadsheetValueStorage, RichTextModule for Word-like editing with IRichTextDocumentProvider and mail merge, PdfViewerModule for PDF display, platform differences (Blazor vs WinForms), programmatic document manipulation. Use when adding file attachments, spreadsheet editing, rich text editing, or PDF viewing to DevExpress XAF applications.
9xaf-reports
XAF Reports Module (XtraReports v2) - ReportsModuleV2 setup for Blazor and WinForms, report storage (DB/filesystem/custom IReportStorageWebExtension), creating predefined reports in code (PredefinedReportsUpdater), data sources (CollectionDataSource/EntityServerModeSource), report parameters, programmatic export (PDF/Excel/Word), in-app designer, PrintAction, security permissions for report design vs view. Use when working with DevExpress XtraReports integration in XAF.
9xaf-editors
XAF built-in property editors and list editors - editor type mapping by data type, EditorAliases constants, [EditorAlias] attribute, [ModelDefault] for DisplayFormat/EditMask, ObjectPropertyEditor for inline sub-forms, list editor types (GridListEditor, DxGridListEditor, TreeListEditor, ChartListEditor), GridListEditor WinForms customization, DxGridListEditor Blazor customization, IModelListView/IModelColumn properties. Use when working with built-in XAF editors or customizing grid/list views.
8