Back to Devexpress

Create a Persistent Object

xpo-2077-create-a-data-model-create-a-persistent-object.md

latest13.9 KB
Original Source

Create a Persistent Object

  • Sep 15, 2020
  • 8 minutes to read

The XPO ORM can load and save to a data store only persistent objects.

You make your business objects persistent in any of the following ways:

Tip

If you use the DevExpress CodeRush tool, you can create a persistent object and use XPO Templates to declare its properties.

This article explains how to derive a business object from the XPObject class to make the object persistent.

Declare a Persistent Object

Derive you business object from the XPObject class. Implement a constructor that receives a Session object as a parameter.

csharp
using DevExpress.Xpo;
// ... 
public class Contact : XPObject {
    // 
    public Contact(Session session) : base(session) { }
    // ... 
}
vb
Imports DevExpress.Xpo
' ...
Public Class Contact : Inherits XPObject

    Public Sub New(ByVal session As Session)
        MyBase.New(session)
    End Sub
    ' ... 
End Class

Declare Properties

Use the PersistentBase.SetPropertyValue method to implement the property’s setter.

csharp
using DevExpress.Xpo;
// ... 
public class Contact : XPObject {
    public Contact(Session session) : base(session) { }

    public string FirstName {
        get { return fFirstName; }
        set { SetPropertyValue(nameof(FirstName), ref fFirstName, value); }
    }
    string fFirstName;

    public string LastName {
        get { return fLastName; }
        set { SetPropertyValue(nameof(LastName), ref fLastName, value); }
    }
    string fLastName;

}
vb
Imports DevExpress.Xpo
' ...
 Public Class Contact : Inherits XPObject
    Public Sub New(ByVal session As Session)
        MyBase.New(session)
    End Sub

    Public Property FirstName() As String
        Get
            Return fFirstName
        End Get
        Set(ByVal value as String)
            SetPropertyValue(NameOf(FirstName), fFirstName, value)
        End Set
    End Property
    Private fFirstName As String

    Public Property LastName() As String
        Get
            Return fLastName
        End Get
        Set(ByVal value as String)
            SetPropertyValue(NameOf(LastName), fLastName, value)
        End Set
    End Property
    Private fLastName As String

End Class

The SetPropertyValue method is designed to shorten the typical set accessor declaration. The method takes three parameters: the name of the property, reference to the property’s private field, and the new property value.

The SetPropertyValue method:

  • checks whether the new property value is different from the previous value. If it is different, the previous value is stored and the property value is set to the new value;
  • triggers the property change event.

Tip

You can use the OnChanged method to implement persistent propert setters. Refer to the method description for more information.

Declare Property Without a Backing Field

You can use the PersistentBase.GetPropertyValue in the property’s getter to omit the property’s private field declaration. In this case, you should use the SetPropertyValue method that takes the property name and the new property value as parameters.

csharp
public string FirstName {
    get { return GetPropertyValue<string>(nameof(FirstName)); }
    set { SetPropertyValue(nameof(FirstName), value); }
}
vb
Public Property FirstName() As String
    Get
        Return GetPropertyValue(Of String)(NameOf(FirstName))
    End Get
    Set
        SetPropertyValue(NameOf(FirstName), Value)
    End Set
End Property

Note

If you use the PersistentBase.GetPropertyValue in the property’s getter, your application’s performance may be worse with large amounts of data since the GetPropertyValue method gets a value from a dictionary.

Properties with a Delayed (Lazy) Loading

XPO objects support lazy instantiation.

Persistent properties mapped to data fields that contain large amounts of data (images, large text documents, or binary data) require a lot of memory. Decorate such properties with the DelayedAttribute to enable delayed loading. XPO does not populate such properties until you call the GetDelayedPropertyValue method.

Refer to the Delayed Loading topic for more information.

Property Change Notifications

All the XPO base classes implement the INotifyPropertyChanged interface.

XPO requires that your business object’s properties fire the PropertyChanged event when their values are changed. The event is fired when you implement property setters with the SetPropertyValue method.

Calculated Properties

To make a property calculated, decorate it with the PersistentAliasAttribute attribute. Pass the calculate expresion (used to evaluate the property value) to the attribute constructor.

The calculated property is a read-only property and has no setter. To fire the PropertyChanged event for a calculated property, call the OnChanged method within the setters of properties the calculated property depends on.

csharp
public class Payment : XPObject {
    public Payment(Session session) : base(session) { }
    private double rate;
    public double Rate {
        get {
            return rate;
        }
        set {
            if(SetPropertyValue(nameof(Rate), ref rate, value))
                // Fires the PropertyChanged event for the Amount property
                OnChanged(nameof(Amount));
        }
    }
    private double hours;
    public double Hours {
        get {
            return hours;
        }
        set {
            if(SetPropertyValue(nameof(Hours), ref hours, value))
                // Fires the PropertyChanged event for the Amount property
                OnChanged(nameof(Amount));
        }
    }
    [PersistentAlias("Rate * Hours")]
    public double Amount {
        get {
            object tempObject = EvaluateAlias(nameof(Amount));
            if(tempObject != null) {
                return (double)tempObject;
            }
            else {
                return 0;
            }
        }
    }
}
vb
<DefaultClassOptions, ImageName("BO_SaleItem")> _
Public Class Payment
    Inherits BaseObject
    Public Sub New(ByVal session As Session)
        MyBase.New(session)
    End Sub
    Private rate_Renamed As Double
    Public Property Rate() As Double
        Get
            Return rate_Renamed
        End Get
        Set(ByVal value As Double)
            If SetPropertyValue(NameOf(Rate), rate_Renamed, value) Then
                ' Fires the PropertyChanged event for the Amount property
                OnChanged(NameOf(Amount))
            End If
        End Set
    End Property
    Private hours_Renamed As Double
    Public Property Hours() As Double
        Get
            Return hours_Renamed
        End Get
        Set(ByVal value As Double)
            If SetPropertyValue(NameOf(Hours), hours_Renamed, value) Then
                ' Fires the PropertyChanged event for the Amount property
                OnChanged(NameOf(Amount))
            End If
        End Set
    End Property
    <PersistentAlias("Rate * Hours")> _
    Public ReadOnly Property Amount() As Double
        Get
            Dim tempObject As Object
            tempObject = EvaluateAlias(NameOf(Amount))
            If tempObject IsNot Nothing Then
                Return CDbl(tempObject)
            Else
                Return 0
            End If
        End Get
    End Property
End Class

Key Field

Every persistent object includes the Oid (object identifier) property, which uniquely identifies the object. The XPObject class used in this article implements automatic key generation. An autogenerated integer key is mapped to the OID field.

Custom Key Field

You can specify a custom key field, for example, if you create a data model for an existing data table that already has a key field.

To specify a custom key field:

  1. Inherit your persistent object from the XPCustomObject or XPLiteObject class instead of XPObject.

  2. Decorate the property that maps to a key field with the KeyAttribute attribute. The attribute’s KeyAttribute.AutoGenerate property specifies whether the key is generated automatically.

Initialize a Persistent Object

Initialization in this section means setting default values for a new XPObject’s properties. Later these properties can be modified and saved into a persistent storage (a physical database). When objects are restored from the persistent storage, property values must not be overridden with default values.

To initialize a persistent object, override the XPObject’s PersistentBase.AfterConstruction method.

The following sample code shows how to override the PersistentBase.AfterConstruction method to set the afterconstructcalled field’s value immediately after the TestConstruct object has been initialized.

csharp
using DevExpress.Xpo;

class TestConstruct : XPObject {
    [NonPersistent]
    public bool afterconstructcalled = false;
    [NonPersistent]
    public bool constructcalled = false;
    public TestConstruct() {
        constructcalled = true;
    }
    public override void AfterConstruction() {
        base.AfterConstruction();
        afterconstructcalled = true;
    }
}
vb
Imports DevExpress.Xpo

Class TestConstruct
   Inherits XPObject
   <NonPersistent()> _
   Public afterconstructcalled As Boolean = False
   <NonPersistent()> _

   Public constructcalled As Boolean = False
   Public Sub New()
      constructcalled = True
   End Sub

   Public Overrides Sub AfterConstruction()
      MyBase.AfterConstruction()
      afterconstructcalled = True
   End Sub

End Class

Create a New Record

The code below creates a Contact table and inserts one record.

csharp
using System.Linq;
using DevExpress.Xpo;
// ...
// Connect to an in-memory source
const string connectionString = @"XpoProvider=InMemoryDataStore;Data Source=.\mydatabase.xml;Read Only=false";
XpoDefault.DataLayer = XpoDefault.GetDataLayer(connectionString, DevExpress.Xpo.DB.AutoCreateOption.DatabaseAndSchema);

// Create and save a new data object
using(var uow = new UnitOfWork()) {
    var contact = new Contact(uow);
    contact.FirstName = "Alice";
    contact.LastName = "Smith";
    uow.CommitChanges();
}
// Read a data object
using(var uow = new UnitOfWork()) {
    var contact = uow.Query<Contact>().FirstOrDefault(c => c.LastName == "Smith");
    Console.WriteLine(contact.FirstName + " " + contact.LastName);
}
vb
Imports System.Linq
Imports DevExpress.Xpo

' Connect to an in-memory source
Const connectionString As String = "XpoProvider=InMemoryDataStore;Data Source=.\mydatabase.xml;Read Only=false"
XpoDefault.DataLayer = XpoDefault.GetDataLayer(connectionString, DevExpress.Xpo.DB.AutoCreateOption.DatabaseAndSchema)

' Create and save a new data object
Using uow = New UnitOfWork()
    Dim contact = New Contact(uow)
    contact.FirstName = "Alice"
    contact.LastName = "Smith"
    uow.CommitChanges()
End Using

' Read a data object
Using uow = New UnitOfWork()
    Dim contact = uow.Query(Of Contact)().FirstOrDefault(Function(c) c.LastName Is "Smith")
    Console.WriteLine(contact.FirstName & " " & contact.LastName)
End Using

Design-Time Capabilities

You can use the DevExpress v25.2 ORM Persistent Object project item template to create a persistent class.

Next Steps

See Also

XPO Templates