Problem
You want to use XPO as an ORM tool. You have an existing hierarchy of business objects that you wish to persist, but you want a more flexible solution than that provided by using attributes.
Solution
Assume we have the very simplistic business object below:-
public class Car {
private string _Make;
public string Make {
get { return _Make; }
set {
_Make = value;
}
}
private string _Model;
public string Model {
get { return _Model; }
set {
_Model = value;
}
}
public Car() {
}
}
After the required modifications, and the addition of some code to test it, the example now looks like this:-
using System;
using DevExpress.Xpo;
using DevExpress.Xpo.Metadata;
using System.Collections;
using DevExpress.Data.Filtering;
class Program {
static void Main(string[] args) {
//GS - Test our hierarchy is now persistent
//GS - Create and save a Car
using (UnitOfWork uow = new UnitOfWork()) {
Car car = new Car(uow) {
Make = "Ford",
Model = "Fiesta"
};
car.Save();
uow.CommitChanges();
}
//GS - Read back the car
using (UnitOfWork uow = new UnitOfWork()) {
Car car = FindCarByMake("Ford", uow);
Console.WriteLine(String.Format(
"Make = {0}, Model = {1}",
car.Make,
car.Model));
}
//GS - Delete the Car
using (UnitOfWork uow = new UnitOfWork()) {
Car car = FindCarByMake("Ford",uow);
car.Delete();
uow.CommitChanges();
}
//GS - Prove the car has been deleted
using (UnitOfWork uow = new UnitOfWork())
{
Car car = FindCarByMake("Ford",uow);
if(car == null){
Console.WriteLine("Car has been deleted!");
}
else{
Console.WriteLine("Something went wrong!");
}
}
}
private static Car FindCarByMake(string make, UnitOfWork uow) {
BinaryOperator bo = new BinaryOperator("Make", make);
return uow.FindObject<Car>(bo);
}
}
//GS - Create an existing hierarchy
public class Car : IXPObject, IComparable {
private string _Make;
public string Make {
get { return _Make; }
set {
_Make = value;
OnChanged("Make");
}
}
private string _Model;
public string Model {
get { return _Model; }
set {
_Model = value;
OnChanged("Model");
}
}
public Car() {
}
//GS - Provide an Id for our object
private int _Id;
[Key(true)]
public int Id {
get { return _Id; }
set {
_Id = value;
OnChanged("Id");
}
}
//GS - Provide a backing store for Session and ClassInfo
private Session _Session;
private XPClassInfo _ClassInfo;
//GS - Provide a constructor for our object that takes a Session object
public Car(Session session) {
_Session = session;
_ClassInfo = session.Dictionary.GetClassInfo(this.GetType());
if (!IsLoading)
AfterConstruction();
}
//GS - Write any post construction code here
private void AfterConstruction() { }
//GS - Create a backing store and a property for the IsLoading bool
bool _IsLoading;
[NonPersistent]
public bool IsLoading { get { return _IsLoading; } }
//GS - Define the Changed event
public event ObjectChangeEventHandler Changed;
//GS - Raise the Change event
protected void RaiseChangeEvent(ObjectChangeEventArgs args) {
if (Changed != null)
Changed(this, args);
}
public void OnChanged() {
if (!IsLoading)
RaiseChangeEvent(new ObjectChangeEventArgs(ObjectChangeReason.Reset));
}
protected void OnChanged(string propertyName) {
if (!IsLoading)
RaiseChangeEvent(
new ObjectChangeEventArgs(ObjectChangeReason.PropertyChanged,
propertyName));
}
//GS - Provide general persistence methods
public void Save() {
Session.Save(this);
}
public void Delete() {
Session.Delete(this);
RaiseChangeEvent(new ObjectChangeEventArgs(ObjectChangeReason.Delete));
}
public void Reload() {
Session.Reload(this);
RaiseChangeEvent(new ObjectChangeEventArgs(ObjectChangeReason.Reset));
}
#region IXPObject Members
public void OnDeleted() {}
public void OnDeleting() {}
public void OnLoaded() {
_IsLoading = false;
}
public void OnLoading() {
_IsLoading = true;
}
public void OnSaved() {}
public void OnSaving() {}
#endregion
#region IXPClassInfoProvider Members
public XPClassInfo ClassInfo {
get { return _ClassInfo; }
}
#endregion
#region IXPDictionaryProvider Members
public XPDictionary Dictionary { get { return Session.Dictionary; } }
#endregion
#region ISessionProvider Members
public Session Session {
get { return _Session; }
}
#endregion
#region IDataLayerProvider Members
public IDataLayer DataLayer {get {return Session.DataLayer;}}
#endregion
#region IComparable Members
public int CompareTo(object obj) {
return Comparer.Default.Compare(Session.GetKeyValue(this),
Session.GetKeyValue(obj as IXPSimpleObject));
}
#endregion
}
Discussion
To make an existing hierarchy persistent, using interfaces, the first thing we do is to modify the the class definition so that it implements the IXPObject and IComparable interfaces. Then we add a property to hold the Id of the object (note the use of the key attribute to specify that the key should be auto generated), and implement change notification in that property and the other properties already defined in our class.
Following that we create a backing store for the Session and ClassInfo objects and implement a constructor, for our class, that sets those fields. We also, at this point, provide a hook for developers to place any intialisation code that must be run post construction.
The next thing we do is to code the change notification that we called from our properties when we made those changes above, before coding the general persistence methods of Save(), Reload() and Delete().
This completes the work we have to do on our own class, and all that remains for us to do is to implement the methods required by the interfaces.
Technorati tags:
XPO Cookbook