Add Persistence to an Existing Hierarchy: Change the Base Inheritance
- 7 minutes to read
Changing the base inheritance of classes requires the IXPObject and IComparable interfaces to be implemented. Compared to the other option of adding persistence to an existing class hierarchy which is discussed in the Add Persistence to an Existing Hierarchy: Session-less Persistent Objects topic, changing the base inheritance of classes represents a more flexible technique for manipulating persistence objects (such as handling the object’s initialization, loading, saving or deletion) and extending their functionality (such as adding collection properties).
Below is a step-by-step guide as to what you need to do.
Changing the Base Inheritance
Providing a Session-Specific Constructor
Session-specific constructor declarations are required. The session and classInfo variables are initialized here for future use by the corresponding properties (see IXPSimpleObject implementation below). Additional construction logic can be implemented in the PersistentBase.AfterConstruction method.
public PersistentObject(Session session) {
this.session = session;
this.classInfo = session.Dictionary.GetClassInfo(this.GetType());
if(!session.IsObjectsLoading)
AfterConstruction();
}
// Implement additional construction logic here
protected void AfterConstruction() {
if (Session is UnitOfWork && ClassInfo.IsPersistent) {
Session.Save(this);
}
}
Implementing an Object Identity Value
To distinguish between the states of persistent objects stored in a database, an object identity value should be used. It’s used to address a specific object’s state. In the following code example one possible implementation of identity values is shown. In a database it will be represented by an auto-generated key field of the integer type.
int persistentID;
[Key(AutoGenerate = true)]
public int Oid {
get { return persistentID; }
set {
persistentID = value;
OnChanged(nameof(Oid));
}
}
Implementing General Persistent Object Manipulation
In order to manipulate persistent objects, the IXPSimpleObject and DevExpress.Xpo.IXPObjectWithChangedEvent events should be implemented. Whenever changes to the object are made, the IXPObjectWithChangedEvent.Changed event should be fired. The type of changes are specified by the ObjectChangeEventArgs instance as shown below.
Session session;
XPClassInfo classInfo;
public Session Session { get { return this.session; } }
public XPClassInfo ClassInfo { get { return this.classInfo; } }
bool changed = false;
public IDataLayer DataLayer { get { return session.DataLayer;} }
public XPDictionary Dictionary { get { return session.Dictionary; } }
protected void RaiseChangeEvent(ObjectChangeEventArgs args) {
if(Changed != null)
Changed(this, args);
}
public void OnChanged() {
changed = true;
if(!IsLoading)
RaiseChangeEvent(new ObjectChangeEventArgs(Session, this, ObjectChangeReason.Reset));
}
protected void OnChanged(string propertyName) {
changed = true;
if(!IsLoading)
RaiseChangeEvent(new ObjectChangeEventArgs(Session, this, ObjectChangeReason.PropertyChanged, propertyName, oldValue, newValue));
}
public event ObjectChangeEventHandler Changed;
// The following three methods implement general persistent operations
public void Save() {
Session.Save(this);
}
public void Delete() {
changed = true;
Session.Delete(this);
RaiseChangeEvent(new ObjectChangeEventArgs(Session, this, ObjectChangeReason.Delete));
}
public void Reload() {
Session.Reload(this);
collections.Clear(); // Required for collection properties (see implementation below)
RaiseChangeEvent(new ObjectChangeEventArgs(Session, this, ObjectChangeReason.Reset));
}
Implementing Persistent Object Comparisons
To enable object comparisons, the IComparable interface should be implemented, this is required to implement sorting. The following code example shows how to do this.
int IComparable.CompareTo(object value) {
return Comparer.Default.Compare(Session.GetKeyValue(this),
Session.GetKeyValue(value as IXPSimpleObject));
}
Handling Changes in Persistent Objects
To perform additional housekeeping after initializing, saving or deleting an object, an IXPObject interface should be implemented. The IsLoading property indicates whether the object is completely loaded.
bool isLoading;
[NonPersistent]
public bool IsLoading { get { return isLoading; } }
// Implement additional logic after an object has been deleted
void IXPObject.OnDeleted() { }
// Implement additional logic when an object is about to be deleted
void IXPObject.OnDeleting() { }
// Implement additional logic after an object has been loaded
void IXPObject.OnLoaded() {
isLoading = false;
}
// Implement additional logic before loading an object
void IXPObject.OnLoading() {
isLoading = true;
}
// Implement additional logic after an object has been saved
void IXPObject.OnSaved() { }
// Implement additional logic before saving an object
void IXPObject.OnSaving() { }
Providing Lookup Editors Support
To provide support for lookup editors, add This property implementation as shown in the following code example.
Adding Collection Properties
To implement collection properties, the following code can be used. Note that the Reload method clears the collection’s contents (see implementation above).
IDictionary collections;
IDictionary Collections {
get {
if(collections == null)
collections = new System.Collections.Specialized.HybridDictionary();
return collections;
}
}
XPMemberInfo GetCollectionProperty(string name) {
XPMemberInfo p = ClassInfo.GetMember(name);
if (p != null)
return p;
throw new PropertyMissingException(ClassInfo.FullName, name);
}
// Retrieves a collection by the name of the property which exposes the collection
protected XPCollection GetCollection(string propertyName) {
XPCollection result = Collections[propertyName] as XPCollection;
if(result == null) {
result = new XPCollection(this.Session, this, GetCollectionProperty(propertyName));
Collections.Add(propertyName, result);
}
return result;
}
}
Implementing the IObjectLayerProvider interface
The following example demonstrates how to provide access to an Object Access Layer which the current Session uses to retrieve and update object data in a data store. This property is required for internal purposes.