Back to Devexpress

Add Persistence to an Existing Hierarchy: Change the Base Inheritance

xpo-2109-create-a-data-model-add-persistence-to-an-existing-hierarchy-change-the-base-inheritance.md

latest14.2 KB
Original Source

Add Persistence to an Existing Hierarchy: Change the Base Inheritance

  • Oct 04, 2022
  • 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

csharp
public class PersistentObject: SuperObject, IXPObject, IComparable {
vb
Public Class PersistentObject
    Inherits SuperObject
    Implements IXPObject
    Implements IComparable

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.

csharp
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);
        }
    }
vb
Public Sub New(ByVal session As Session)
        Me._session = session
        Me._classInfo = session.Dictionary.GetClassInfo(Me.GetType())
        If Not Me._session.IsObjectsLoading Then
            AfterConstruction()
        End If
    End Sub

    ' Implement additional construction logic here
    Protected Sub AfterConstruction()
        If TypeOf Session Is UnitOfWork AndAlso ClassInfo.IsPersistent Then
            Session.Save(Me)
        End If
    End Sub

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.

csharp
int persistentID;
    [Key(AutoGenerate = true)]
    public int Oid {
        get { return persistentID; }
        set { 
            persistentID = value;
            OnChanged(nameof(Oid));
        }
    }
vb
Dim persistentID As Integer
    <Key(AutoGenerate:=True)> _
    Public Property Oid() As Integer
        Get
            Return persistentID
        End Get
        Set(ByVal Value As Integer)
            persistentID = Value
            OnChanged(NameOf(Oid))
        End Set
    End Property

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.

csharp
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));
    }
vb
Dim _session As Session
    Dim _classInfo As XPClassInfo
    Public ReadOnly Property Session() As Session Implements IXPSimpleObject.Session
        Get
            Return Me._session
        End Get
    End Property
    Public ReadOnly Property ClassInfo() As XPClassInfo Implements IXPSimpleObject.ClassInfo
        Get
            Return Me._classInfo
        End Get
    End Property
    Dim _changed As Boolean = False

    Public ReadOnly Property DataLayer() As IDataLayer
        Get
            Return Me._session.DataLayer
        End Get
    End Property
    Public ReadOnly Property Dictionary() As XPDictionary
        Get
            Return Me._session.Dictionary
        End Get
    End Property

    Protected Sub RaiseChangeEvent(ByVal args As ObjectChangeEventArgs)
        RaiseEvent Changed(Me, args)
    End Sub
    Public Sub OnChanged()
        _changed = True
        If Not IsLoading Then
            RaiseChangeEvent(New ObjectChangeEventArgs(Session, Me, ObjectChangeReason.Reset))
        End If
    End Sub
    Protected Sub OnChanged(ByVal propertyName As String)
        _changed = True
        If Not IsLoading Then
            RaiseChangeEvent(New ObjectChangeEventArgs(Session, Me, ObjectChangeReason.PropertyChanged, propertyName, oldValue, newValue))
        End If
    End Sub
    Public Event Changed As ObjectChangeEventHandler Implements IXPSimpleObject.Changed

    ' The following three methods implement general persistent operations
    Public Sub Save()
        Session.Save(Me)
    End Sub
    Public Sub Delete()
        _changed = True
        Session.Delete(Me)
        RaiseChangeEvent(New ObjectChangeEventArgs(Session, Me, ObjectChangeReason.Delete))
    End Sub
    Public Sub Reload()
        Session.Reload(Me)
        _collections.Clear() ' Required for collection properties (see implementation below)
        RaiseChangeEvent(New ObjectChangeEventArgs(Session, Me, ObjectChangeReason.Reset))
    End Sub

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.

csharp
int IComparable.CompareTo(object value) {
        return Comparer.Default.Compare(Session.GetKeyValue(this), 
          Session.GetKeyValue(value as IXPSimpleObject));
    }
vb
Public Overridable Overloads Function CompareTo(ByVal value As Object) As Integer _
    Implements IComparable.CompareTo
        Return Comparer.Default.Compare(Session.GetKeyValue(Me), 
          Session.GetKeyValue(CType(value, IXPSimpleObject)))
    End Function

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.

csharp
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() { }
vb
Dim _isLoading As Boolean
    <NonPersistent()> _
    Public ReadOnly Property IsLoading() As Boolean
        Get
            Return _isLoading
        End Get
    End Property
    ' Implement additional logic after an object has been deleted
    Sub OnDeleted() Implements IXPObject.OnDeleted
    End Sub
    ' Implement additional logic when an object is about to be deleted
    Sub OnDeleting() Implements IXPObject.OnDeleting
    End Sub
    ' Implement additional logic after an object has been loaded
    Sub OnLoaded() Implements IXPObject.OnLoaded
        _isLoading = False
    End Sub
    ' Implement additional logic before loading an object
    Sub OnLoading() Implements IXPObject.OnLoading
        _isLoading = True
    End Sub
    ' Implement additional logic after an object has been saved
    Sub OnSaved() Implements IXPObject.OnSaved
    End Sub
    ' Implement additional logic before saving an object
    Sub OnSaving() Implements IXPObject.OnSaving
    End Sub

Providing Lookup Editors Support

To provide support for lookup editors, add This property implementation as shown in the following code example.

csharp
public object This { get { return this; } }
vb
Public ReadOnly Property This() As Object
        Get
            Return Me
        End Get
    End Property

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).

csharp
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;
    }
}
vb
Dim _collections As IDictionary
    ReadOnly Property Collections() As IDictionary
        Get
            If _collections Is Nothing Then
                _collections = New System.Collections.Specialized.HybridDictionary()
            End If
            Return _collections
        End Get
    End Property
    Function GetCollectionProperty(ByVal name As String) As XPMemberInfo
        Dim p As XPMemberInfo = ClassInfo.GetMember(name)
        If Not p Is Nothing Then
            Return p
        End If
        Throw New PropertyMissingException(ClassInfo.FullName, name)
    End Function
    ' Retrieves a collection by the name of the property which exposes the collection
    Protected Function GetCollection(ByVal propertyName As String) As XPCollection
        Dim result As XPCollection = CType(Collections.Item(propertyName), XPCollection)
        If result Is Nothing Then
            result = New XPCollection(Me.Session, Me, GetCollectionProperty(propertyName))
            Collections.Add(propertyName, result)
        End If
        Return result
    End Function
End Class

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.

csharp
IObjectLayer IObjectLayerProvider.ObjectLayer {
        get { return Session.ObjectLayer; }
    }
vb
Private ReadOnly Property ObjectLayer() As IObjectLayer Implements IObjectLayerProvider.ObjectLayer
        Get
            Return Session.ObjectLayer
        End Get
    End Property

See Also

Add Persistence to an Existing Hierarchy: Session-less Persistent Objects

How to: Handle Persistent Object Initialization

IXPObject

IXPSimpleObject