How to: Show Persistent Objects in a Non-Persistent Object's View
- 6 minutes to read
This topic describes how to declare a reference or collection property of a persistent type in a non-persistent class and display it in the UI.
Persistent Reference Property
Implement the following non-persistent class:
using DevExpress.ExpressApp.DC;
using DevExpress.Persistent.Base;
using DevExpress.Persistent.BaseImpl;
// ...
[DomainComponent, DefaultClassOptions]
public class NonPersistentObject {
// ...
public string Name { get; set; }
public Person Owner { get; set; }
// ...
}
Note
The INotifyPropertyChanged, IXafEntityObject and IObjectSpaceLink interface implementations were omitted in this example. However, it is recommended to support these interfaces in real-world applications (see The Importance of Property Change Notifications for Automatic UI Updates and Non-Persistent Objects).
Tip
Use the approach demonstrated in the following topic to support save and load operations for non-persistent objects: How to: Perform CRUD Operations with Non-Persistent Objects.
In this example, Person
is a persistent class from the Business Class Library. You can use a custom business class instead of Person
.
Run the application and create a new NonPersistentObject
. In its Detail View, the lookup editor for the Owner
property is empty and you cannot choose an existing Person
.
The NonPersistentObjectSpace created for the current View cannot process the Person
persistent object. Follow the steps below to create a persistent Object Space for this type.
- Handle the
OnObjectSpaceCreated
event in the Application Builder code in the application’sStartup.cs
file. - In the event handler, call the PopulateAdditionalObjectSpaces(IObjectSpaceProviderService, IObjectSpaceCustomizerService) method to populate the AdditionalObjectSpaces collection with an additional Object Space that can handle the
Person
type.
File: MySolution.Blazor.Server/Startup.cs, MySolution.Win/Startup.cs, MySolution.WebApi/Startup.cs
using DevExpress.ExpressApp;
//...
builder.ObjectSpaceProviders.Events.OnObjectSpaceCreated = context => {
CompositeObjectSpace compositeObjectSpace = context.ObjectSpace as CompositeObjectSpace;
if (compositeObjectSpace != null) {
if (!(compositeObjectSpace.Owner is CompositeObjectSpace)) {
var objectSpaceProviderService = context.ServiceProvider.GetRequiredService<IObjectSpaceProviderService>();
var objectSpaceCustomizerService = context.ServiceProvider.GetRequiredService<IObjectSpaceCustomizerService>();
compositeObjectSpace.PopulateAdditionalObjectSpaces(objectSpaceProviderService, objectSpaceCustomizerService);
}
}
}
The image below demonstrates the result.
Persistent Collection
You can add the Owners
collection instead of the Owner
reference property, as shown in the code snippet below.
[DomainComponent, DefaultClassOptions]
public class NonPersistentObject{
// ...
public string Name { get; set; }
private IList<Person> owners = new List<Person>();
public IList<Person> Owners {
get {
return owners;
}
}
}
To allow users to add and remove Owners
with the Link
and Unlink
Actions, create an additional Object Space for the Person
type, as demonstrated in the previous section.
For a more complex example, refer to the following GitHub repository: How to edit a collection of persistent objects linked to a non-persistent object.
Initialize Persistent Property Values
Implement the IObjectSpaceLink interface in your non-persistent class and use the IObjectSpace.GetObjects<T> method to get persistent objects.
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using DevExpress.Data.Filtering;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.DC;
using DevExpress.Persistent.Base;
using DevExpress.Persistent.BaseImpl;
// ...
[DomainComponent, DefaultClassOptions]
public class NonPersistentObject : IObjectSpaceLink {
public string Name { get; set; }
private Person owner;
public Person Owner {
get {
if (owner == null) {
owner = ObjectSpace.GetObjects<Person>(CriteriaOperator.Parse("FirstName='Sam'")).FirstOrDefault();
}
return owner;
}
set { owner = value; }
}
private IList<Person> owners;
public IList<Person> Owners {
get {
if (owners == null) {
owners = ObjectSpace.GetObjects<Person>(CriteriaOperator.Parse("StartsWith(FirstName, 'B')")) ;
}
return owners;
}
internal set { owners = value; }
}
private IObjectSpace objectSpace;
[Browsable(false)]
public IObjectSpace ObjectSpace {
get { return objectSpace; }
set { objectSpace = value; }
}
}
If you create a new NonPersistentObject
in the UI, its Owner
property and Owner
collection are initialized.
Refresh Linked Persistent Objects
The Refresh Action does not affect non-persistent object Views. Follow the steps below to allow non-persistent objects and persistent objects linked to them to reload.
- Access the NonPersistentObjectSpace and set its AutoRefreshAdditionalObjectSpaces property to
true
. - Subscribe to the NonPersistentObjectSpace.ObjectGetting event. In the event handler, create a new instance of the non-persistent object and copy property values from the source object. To create copies of persistent objects linked to non-persistent objects, use the IObjectSpace.GetObject method.
File: MySolution.Blazor.Server/Startup.cs, MySolution.Win/Startup.cs, MySolution.WebApi/Startup.cs
using DevExpress.ExpressApp;
using System.ComponentModel;
// ...
builder.ObjectSpaceProviders.Events.OnObjectSpaceCreated = context => {
CompositeObjectSpace compositeObjectSpace = context.ObjectSpace as CompositeObjectSpace;
if (compositeObjectSpace != null) {
if (!(compositeObjectSpace.Owner is CompositeObjectSpace)) {
var objectSpaceProviderService = context.ServiceProvider.GetRequiredService<IObjectSpaceProviderService>();
var objectSpaceCustomizerService = context.ServiceProvider.GetRequiredService<IObjectSpaceCustomizerService>();
compositeObjectSpace.PopulateAdditionalObjectSpaces(objectSpaceProviderService, objectSpaceCustomizerService);
}
}
NonPersistentObjectSpace nonPersistentObjectSpace = e.ObjectSpace as NonPersistentObjectSpace;
if (nonPersistentObjectSpace != null) {
nonPersistentObjectSpace.AutoRefreshAdditionalObjectSpaces = true;
nonPersistentObjectSpace.ObjectGetting += NonPersistentObjectSpace_ObjectGetting;
}
}
// ...
private void NonPersistentObjectSpace_ObjectGetting(object sender, ObjectGettingEventArgs e) {
var objectSpace = (IObjectSpace)sender;
if (e.SourceObject is NonPersistentObject) {
var sourceObject = (NonPersistentObject)e.SourceObject;
var targetObject = new NonPersistentObject();
targetObject.Owner = objectSpace.GetObject<Person>(sourceObject.Owner);
var owners = new List<Person>();
foreach (var owner in sourceObject.Owners) {
owners.Add(objectSpace.GetObject<Person>(owner));
}
targetObject.Owners = owners;
e.TargetObject = targetObject;
}
}
If you modify and save a Person
in another View or in a database while the NonPersistentObject
Detail View is open, click the Refresh Action to refresh data in this View.
For a more complex example of a non-persistent object with linked persistent objects, refer to the following GitHub example: How to refresh Non-Persistent Objects and reload nested Persistent Objects.
Important Notes
If a non-persistent object implements IObjectSpaceLink and its ObjectSpace property value doesn’t match the Object Space that manipulates the object, the following exception is thrown: ‘An error with the number 1021 has occurred. Error message: The object that was edited belongs to a different ObjectSpace. This error can occur if you manipulate your objects via an ObjectSpace to which these objects do not belong.’
We recommend that you do not share non-persistent objects when they implement IObjectSpaceLink or have linked persistent objects because of the following limitations:
- You can use a shared (static) storage for non-persistent objects only if a single NonPersistentObjectSpace works with these objects at any time. Non-persistent objects that implement IObjectSpaceLink cannot belong to multiple Object Spaces simultaneously. When you change the IObjectSpaceLink.ObjectSpace property to a different Object Space, dispose of the previously used Object Space. Otherwise, object changes can trigger events in multiple Views and cause conflicts or unexpected behavior.
- You cannot share persistent objects between Object Spaces.
Instead of sharing non-persistent objects, create new instances in each NonPersistentObjectSpace. If you want to preserve data from all instances of a non-persistent object, store this data separately. See the example in the MainDemo.Module\NonPersistentObjects\NonPersistentObjectSpaceExtender.cs_ file in the Feature Center demo that is installed in the following folder: %PUBLIC%\Documents\DevExpress Demos 24.1\Components\XAF\MainDemo.NET.EFCore\CS.
For more information, refer to the following Breaking Change description: Core - Error 1021 may occur if a non-persistent object implements IObjectSpaceLink and is shared between different Object Spaces.