Create a Persistent Object
- 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:
- 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 Code
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.
using DevExpress.Xpo;
// ...
public class Contact : XPObject {
//
public Contact(Session session) : base(session) { }
// ...
}
#Declare Properties
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;
}
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 On
#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.
public string FirstName {
get { return GetPropertyValue<string>(nameof(FirstName)); }
set { SetPropertyValue(nameof(FirstName), value); }
}
Note
If you use the Persistent
in the property’s getter, your application’s performance may be worse with large amounts of data since the Get
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.
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;
}
}
}
}
#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:
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.
public partial class Contact : XPLiteObject { int fContactKey; [Key(true)] public int ContactKey { get { return fContactKey; } set { SetPropertyValue<int>(nameof(ContactKey), ref fContactKey, value); } } string fFirstName; [Size(50)] public string FirstName { get { return fFirstName; } set { SetPropertyValue<string>(nameof(FirstName), ref fFirstName, value); } } string fLastName; [Size(50)] public string LastName { get { return fLastName; } set { SetPropertyValue<string>(nameof(LastName), ref fLastName, value); } } public Contact(Session session) : base(session) { } }
#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.
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;
}
}
#Create a New Record
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);
}
#Design-Time Capabilities
You can use the DevExpress v24.1 ORM Persistent Object project item template to create a persistent class.