xpo-2077-create-a-data-model-create-a-persistent-object.md
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:
derive an object from any of the following base classes (XPO Classes Comparison):
implement IXPObject and IComparable interfaces in your object,
decorate your object with the PersistentAttribute and specify a key field.
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.
Derive you business object from the XPObject class. Implement a constructor that receives a Session object as a parameter.
using DevExpress.Xpo;
// ...
public class Contact : XPObject {
//
public Contact(Session session) : base(session) { }
// ...
}
Imports DevExpress.Xpo
' ...
Public Class Contact : Inherits XPObject
Public Sub New(ByVal session As Session)
MyBase.New(session)
End Sub
' ...
End Class
Use the PersistentBase.SetPropertyValue method to implement the property’s setter.
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;
}
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:
Tip
You can use the OnChanged method to implement persistent propert setters. Refer to the method description for more information.
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.
public string FirstName {
get { return GetPropertyValue<string>(nameof(FirstName)); }
set { SetPropertyValue(nameof(FirstName), value); }
}
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.
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.
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.
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.
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;
}
}
}
}
<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
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.
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:
Inherit your persistent object from the XPCustomObject or XPLiteObject class instead of XPObject.
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.
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.
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;
}
}
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
The code below creates a Contact table and inserts one record.
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);
}
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
You can use the DevExpress v25.2 ORM Persistent Object project item template to create a persistent class.
See Also