Skip to main content
.NET 6.0+

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.

PersistentInNonPresistent1

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.

  1. Handle the OnObjectSpaceCreated event in the Application Builder code in the application’s Startup.cs file.
  2. 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.

PersistentInNonPresistent2

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.

  1. Access the NonPersistentObjectSpace and set its AutoRefreshAdditionalObjectSpaces property to true.
  2. 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.

See Also