Skip to main content
.NET 8.0+

How to: Link Classes Located in Different Assemblies

  • 5 minutes to read

This topic describes how to implement relationships between XPO classes declared in different assemblies.

Data Model

The examples in this topic use the following data model:

Assembly: ClassLibrary1.dll

using DevExpress.Xpo;

namespace ClassLibrary1 {
    public class Order : XPObject {
        public Order(Session session) : base(session) { }

        private int orderNumber;
        public int OrderNumber {
            get { return orderNumber; }
            set { SetPropertyValue(nameof(OrderNumber), ref orderNumber, value); }
        }
    }
}

Assembly: ClassLibrary2.dll

using DevExpress.Xpo;

namespace ClassLibrary2 {
    public class Customer : XPObject {
        public Customer(Session session) : base(session) { }

        private string fullName;
        public string FullName {
            get { return fullName; }
            set { SetPropertyValue(nameof(FullName), ref fullName, value); }
        }
    }
}

Derived Class

You can use XPO inheritance to implement relationships between classes from the current assembly (ClassLibrary2) and classes from an external class library (ClassLibrary1). Use this technique when the external library declares XPO classes without additional business logic. Applications that share this library should connect to different databases.

In this example, the CustomerOrder class inherits the Order class from another assembly. The Customer class has a collection of related CustomerOrder classes. In this case, an application should use the CustomerOrder class rather than the Order class to implement operations with orders:

Assembly: ClassLibrary2.dll

using DevExpress.Xpo;

namespace ClassLibrary2 {
    public class Customer : XPObject {
        public Customer(Session session) : base(session) { }
        //...
        [Association, Aggregated]
        public XPCollection<CustomerOrder> Orders {
            get { return GetCollection<CustomerOrder>(nameof(Orders)); }
        }
    }
    [MapInheritance(MapInheritanceType.ParentTable)]
    public class CustomerOrder : ClassLibrary1.Order {
        public CustomerOrder(Session session) : base(session) { }

        private Customer customer;
        [Association]
        public Customer Customer {
            get { return customer; }
            set { SetPropertyValue(nameof(Customer), ref customer, value); }
        }
    }
}

Intermediate Class

This technique allows you to implement a many-to-many relationship in one assembly (ClassLibrary2) without changing classes from another assembly (ClassLibrary1). In this example, the CustomerToOrderLink class serves as a link between the Customer and Order classes. The Customer class uses a collection of CustomerToOrderLink objects to get related Order instances:

Assembly: ClassLibrary2.dll

using DevExpress.Xpo;

namespace ClassLibrary2 {
    public class Customer : XPObject {
        public Customer(Session session) : base(session) { }
        //...
        [Association, Aggregated]
        public XPCollection<CustomerToOrderLink> Orders {
            get { return GetCollection<CustomerToOrderLink>(nameof(Orders)); }
        }
    }
    public class CustomerToOrderLink : XPObject {
        public CustomerToOrderLink(Session session) : base(session) { }

        private Customer customer;
        [Association]
        public Customer Customer {
            get { return customer; }
            set { SetPropertyValue(nameof(Customer), ref customer, value); }
        }

        private ClassLibrary1.Order order;
        public ClassLibrary1.Order Order {
            get { return order; }
            set { SetPropertyValue(nameof(Order), ref order, value); }
        }
    }
}

Dynamic Members

This technique allows you to implement one-to-many and many-to-many relationships between classes without changing their source code. It uses XPClassInfo to create members dynamically at runtime. Refer to the following topic for more information: XPClassInfo.CreateMember Method.

This example adds the Orders collection to the Customer class, and the Customer reference to the Order class. Call this code on an application startup before you initialize a data layer.

using DevExpress.Xpo;
using DevExpress.Xpo.DB;
using DevExpress.Xpo.Metadata;
using ClassLibrary1;
using ClassLibrary2;

ReflectionDictionary dictionary = new ReflectionDictionary();
dictionary.CollectClassInfos(typeof(Order), typeof(Customer));

XPClassInfo customerInfo = dictionary.QueryClassInfo(typeof(Customer));
XPClassInfo orderInfo = dictionary.QueryClassInfo(typeof(Order));
customerInfo.CreateMember("Orders", typeof(XPCollection<Order>), true, false, new AssociationAttribute("Customer-Orders"), new AggregatedAttribute());
orderInfo.CreateMember("Customer", customerInfo, new AssociationAttribute("Customer-Orders"));

XpoDefault.DataLayer = XpoDefault.GetDataLayer(connectionString, dictionary, AutoCreateOption.DatabaseAndSchema);

These members are available in the UI of controls bound to XPO data sources, such as XPCollection and XPServerCollectionSource. They are not available in LINQ sources (XPQuery, LinqServerModeSource).

Use the XPClassInfo.FindMember method to access members in code and the XPMemberInfo.GetValue and XPMemberInfo.SetValue methods to get and change their values.