Description:
I have a common library with common objects and several libraries that are specific to applications. How can I define a collection property in my specific classes which references objects from the common library without changing the common objects?
Answer:
Suppose you have a Role class and it is the most common class which will be used by many other classes, for instance, by Contact, Company, Customer, and User. Each of these should have a collection of linked roles. With XPO you must define every relation in your code, i.e. Role must have the Contact, Company, Customer, and User properties. In addition, you may be planning to introduce several other classes, which should have a collection of Roles. This means that the Role class should be extended with new properties.
To avoid the Role class growing in this way, you may introduce a new object to define a one-to-many association, for instance, for the Customer class it may be a CustomerToRole class with two properties: Customer and Role. Thus, the customer class should have a collection of CustomerToRole objects.
Note. This solution is not quite good. If an association property cannot be added to the class definition, then it's best to add it to the metadata. For more information, see "XPClassInfo.CreateMember Method" topic at http://www.devexpress.com/Help/?document=XPO/DevExpressXpoMetadataXPClassInfo_CreateMembertopic262.htm.
C#using System;
using DevExpress.Xpo;
using DevExpress.Data.Filtering;
public class Role : XPObject {
public string Name = string.Empty;
public string Description = string.Empty;
public byte[] PermissionData;
public Role() {}
public Role(string name) {
Name = name;
}
public Role(Session session) : base(session) {}
}
public class Customer : XPObject {
public string Name;
public Customer() {}
public Customer(string name) {
Name = name;
}
public Customer(Session session) : base(session) {}
[Association("CustomerRoles", typeof(CustomerToRole)), Aggregated]
internal XPCollection RoleLinks {
get {
return GetCollection("RoleLinks");
}
}
RolesCollection roles;
public RolesCollection Roles {
get {
if(roles == null)
roles = new RolesCollection(this);
roles.Reload();
return roles;
}
}
}
public class CustomerToRole : XPObject {
[Association("CustomerRoles")]
public Customer Customer;
public Role Role;
public CustomerToRole() : this(null) {}
public CustomerToRole(Session session) : base(session) {}
}
public class RolesCollection : XPCollection {
Customer customer;
public RolesCollection(Customer customer): base(customer.Session, typeof(Role)) {
this.customer = customer;
LoadingEnabled = false;
}
bool isInternalLoading;
public override void Reload() {
base.Reload();
if(isInternalLoading) return;
isInternalLoading = true;
if(customer.RoleLinks.Count > 0) {
object[] roles = new object[customer.RoleLinks.Count];
for(int i = 0; i < customer.RoleLinks.Count; i++)
roles[i] = ((CustomerToRole)customer.RoleLinks[i]).Role;
this.AddRange(roles);
}
isInternalLoading = false;
}
protected override void OnCollectionChanged(XPCollectionChangedEventArgs args) {
if(!isInternalLoading && args.ChangedObject is Role) {
if(args.CollectionChangedType == XPCollectionChangedType.BeforeAdd) {
CustomerToRole link = new CustomerToRole(Session);
link.Customer = customer;
link.Role = (Role)args.ChangedObject;
customer.RoleLinks.Add(link);
customer.Save();
}
if(args.CollectionChangedType == XPCollectionChangedType.BeforeRemove) {
CustomerToRole link = Session.FindObject(typeof(CustomerToRole), new GroupOperator(
new BinaryOperator("Customer", customer), new BinaryOperator("Role", args.ChangedObject))) as CustomerToRole;
if(link != null) {
customer.RoleLinks.Remove(link);
customer.Save();
link.Delete();
}
}
}
base.OnCollectionChanged(args);
}
}
Visual BasicImports System
Imports DevExpress.Xpo
Imports DevExpress.Data.Filtering
Public Class Role
Inherits XPObject
Public Name As String = String.Empty
Public Description As String = String.Empty
Public PermissionData() As Byte
Public Sub New()
End Sub
Public Sub New(ByVal session As Session)
MyBase.New(session)
End Sub
End Class
Public Class Customer
Inherits XPObject
Public Name As String
Public Sub New()
End Sub
Public Sub New(ByVal session As Session)
MyBase.New(session)
End Sub
<Association("CustomerRoles", GetType(CustomerToRole)), Aggregated()> _
Friend ReadOnly Property RoleLinks() As XPCollection
Get
Return GetCollection("RoleLinks")
End Get
End Property
Private rolesValue As RolesCollection
Public ReadOnly Property Roles() As RolesCollection
Get
If rolesValue Is Nothing Then
rolesValue = New RolesCollection(Me)
End If
rolesValue.Reload()
Return rolesValue
End Get
End Property
End Class
Public Class CustomerToRole
Inherits XPObject
<Association("CustomerRoles")> _
Public Customer As Customer
Public Role As Role
Public Sub New()
MyClass.New(Nothing)
End Sub
Public Sub New(ByVal session As Session)
MyBase.New(session)
End Sub
End Class
Public Class RolesCollection
Inherits XPCollection
Private customer As customer
Public Sub New(ByVal customer As customer)
MyBase.New(customer.Session, GetType(Role))
Me.customer = customer
LoadingEnabled = False
End Sub
Private isInternalLoading As Boolean
Public Overrides Sub Reload()
MyBase.Reload()
If isInternalLoading Then
Return
End If
isInternalLoading = True
If customer.RoleLinks.Count > 0 Then
Dim roles() As Object = New Object(customer.RoleLinks.Count - 1) {}
For i As Integer = 0 To customer.RoleLinks.Count - 1
roles(i) = CType(customer.RoleLinks(i), CustomerToRole).Role
Next
Me.AddRange(roles)
End If
isInternalLoading = False
End Sub
Protected Overrides Sub OnCollectionChanged(ByVal args As XPCollectionChangedEventArgs)
If Not isInternalLoading AndAlso (TypeOf args.ChangedObject Is Role) Then
If args.CollectionChangedType = XPCollectionChangedType.BeforeAdd Then
Dim link As CustomerToRole = New CustomerToRole(Session)
link.Customer = customer
link.Role = CType(args.ChangedObject, Role)
customer.RoleLinks.Add(link)
customer.Save()
End If
If args.CollectionChangedType = XPCollectionChangedType.BeforeRemove Then
Dim link As CustomerToRole = Session.FindObject(GetType(CustomerToRole), _
New GroupOperator(New BinaryOperator("Customer", customer), New BinaryOperator("Role", args.ChangedObject)))
If Not link Is Nothing Then
customer.RoleLinks.Remove(link)
customer.Save()
link.Delete()
End If
End If
End If
MyBase.OnCollectionChanged(args)
End Sub
End Class
See Also:
How to create an empty XPCollection of a specified type
How to define a strongly typed collection property