• 5 minutes to read

When using persistent class hierarchies, you often have to combine base and derived classes in a single query. However, this is challenging since the derived classes obviously have an extended set of properties. The article below shows how to reference these properties using the Upcasting feature.

Suppose we have the following persistent class hierarchy. The CustomerBase class is the base class that implements properties that are common to all customers. The CustomerRegistered and CustomerTrialed classes extend the base functionality by implementing customer specific properties. This hierarchy is displayed in the image below.


The code below describes the persistent class hierarchy:

Persistent Class Hierarchy

using DevExpress.Xpo;

public class CustomerBase : XPObject {
    string fCustomerName;
    private string fEmail;
    public CustomerBase(Session session) : base(session) { }

    public string CustomerName {
        get { return fCustomerName; }
        set { SetPropertyValue(nameof(CustomerName), ref fCustomerName, value); }
    public string Email {
        get { return fEmail; }
        set { SetPropertyValue(nameof(Email), ref fEmail, value); }

public class CustomerRegistered : CustomerBase {
    string fOwnedProducts;
    public CustomerRegistered(Session session) : base(session) { }

    public string OwnedProducts {
        get { return fOwnedProducts; }
        set { SetPropertyValue(nameof(OwnedProducts), ref fOwnedProducts, value); }

public class CustomerTrialed : CustomerBase {
    string fTrialedProducts;
    public CustomerTrialed(Session session) : base(session) { }

    public string TrialedProducts {
        get { return fTrialedProducts; }
        set { SetPropertyValue(nameof(TrialedProducts), ref fTrialedProducts, value); }

Now, you may wish to create a collection of all customers, including those of the derived type, and access all their properties. This can be accomplished by creating a collection of the base class type, as shown below.

XPCollection<CustomerBase> allCustomers = new XPCollection<CustomerBase>(session1);

Since the type of this collection is CustomerBase, you can only access properties of this type. You are not able to access, for instance, the OwnedProducts property even if the collection contains CustomerRegistered objects. This is because the OwnedProducts property is not known to the base class type.

To overcome the limitation, use the Upcasting feature. All you have to do is modify the collection's XPBaseCollection.DisplayableProperties property to something like this: "Oid;CustomerName;<CustomerRegistered>OwnedProducts". Here, the "Oid;CustomerName" is a part of the property value - you get it by default for the CustomerBase class. The <CustomerRegistered>OwnedProducts references the property of a type that differs from the base class type. The required type is enclosed in angle brackets.

The same syntax can also be used when building criteria. For instance, to retrieve all the customers who have bought or evaluated XtraGrid, use the following code.

XPCollection<CustomerBase> gridCustomers = new XPCollection<CustomerBase>(session1, 
"<CustomerRegistered>OwnedProducts = 'XtraGrid' or <CustomerTrialed>TrialedProducts = 'XtraGrid'"

Use the following syntax to upcast reference type properties.

public class Invoice : XPObject {
    CustomerBase fCustomer;
    public Invoice(Session session) : base(session) { }

    // This is a reference type property. It can reference any CustomerBase descendant.
    public CustomerBase Customer {
        get { return fCustomer; }
        set { SetPropertyValue(nameof(Customer), ref fCustomer, value); }

// Uses upcasting to access CustomerRegistered properties.
XPCollection<Invoice> invoices = new XPCollection<Invoice>(session1,
  CriteriaOperator.Parse("Customer.<CustomerRegistered>OwnedProducts = 'XtraGrid'"));

The code above retrieves a collection of invoices for registered customers who have bought XtraGrid.



The Upcasting feature requires all the necessary tables to be created in the database. That is the database schema must exactly match the persistent classes used in your application.

See Also