Tuesday, June 2, 2009

Automatic properties for Xpo

 

The problem

Xpo in order to support transactions implements some special kind of property setters like

private string lastName;
public string LastName
{
get
{
return lastName;
}
set
{
string oldValue = lastName;
if (oldValue == value)
return;
lastName = value;
OnChanged("Name", oldValue, value);
}
}

Or a little more elegant like

private string lastName;
public string LastName
{
get
{
return lastName;
}
set
{
SetPropertyValue("LastName", ref lastName, value);
}
}

Imagine you want to Design a fairly simple model like Customer –> Orders. A Customer should have LastName, FilrstName, Age and Orders as properties and Order only an Amount property. Using the above approach one could write the following code to describe the above model.

public class Customer : BaseObject
{
public Customer(Session session) : base(session) { }
private string firstName;
public string FirstName
{
get
{
return firstName;
}
set
{
SetPropertyValue("FirstName", ref firstName, value);
}
}

private string lastName;
public string LastName
{
get
{
return lastName;
}
set
{
SetPropertyValue("LastName", ref lastName, value);
}
}

private int age;
public int Age
{
get
{
return age;
}
set
{
SetPropertyValue("Age", ref age, value);
}
}
[Association("CustomerOrders")]
public XPCollection<Order> Orders
{
get
{
return GetCollection<Order>("Orders");
}
}
}

public class Order:BaseObject
{
private int ammount;
public int Ammount
{
get
{
return ammount;
}
set
{
SetPropertyValue("Ammount", ref ammount, value);
}
}
private Customer customer;
[Association("CustomerOrders")]
public Customer Customer
{
get
{
return customer;
}
set
{
SetPropertyValue("Customer", ref customer, value);
}
}
}

Damn!!! That was too long for describing that simple model. Wouldn’t it be nicer for someone to write something like the code bellow using automatic properties to describe exactly the same  model?

public class Customer : BaseObject
{
public Customer(Session session) : base(session) { }

public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }

[Association("CustomerOrders")]
public XPCollection<Order> Orders
{
get;private set;
}
}

public class Order:BaseObject
{
public int Ammount { get; set; }

[Association("CustomerOrders")]
public Customer Customer { get; set; }
}


The solution


Yes it’s time to use AOP PostSharp and XpoAutomaticProperties open source frameworks to achieve our goal. Do not be afraid it’s actually very simple you only have to decorate your object with an attribute and implement an IPropertyChangedNotifier interface.I am sure that most of you have a BaseObject descendant so let’s add that interface to that object

[NonPersistent]
public abstract class CustomBaseObject:BaseObject,IPropertyChangedNotifier
{
protected CustomBaseObject(Session session) : base(session)
{
}

protected CustomBaseObject()
{
}

void IPropertyChangedNotifier.OnChanged(string propertyName, object oldValue, object newValue)
{
base.OnChanged(propertyName, oldValue, newValue);
}
}

Now lets change the base class of our objects from BaseObject to CustomBaseObject and decorate them with XpoAutomaticProperties attribute

[XpoAutomaticProperties]
public class Customer : CustomBaseObject
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }

[Association("CustomerOrders")]
public XPCollection<Order> Orders
{
get;private set;
}
}
[XpoAutomaticProperties]
public class Order : CustomBaseObject
{
public int Ammount { get; set; }

[Association("CustomerOrders")]
public Customer Customer { get; set; }
}

That’s it!. Still not convinced that it works? I will write a simple test that confirms that

public void Test_That_Change_Event_Is_Raised()
{
var customer = new Customer();
bool fired= false;

customer.Changed += (sender, args) => fired = true;
customer.Age++;

Assert.AreEqual(true, fired);
}
[Test]
public void Test_That_Change_Event_Do_Not_Raizes_When_XpoIgnoreAutomaticPropertyAttribute_IsApplied()
{
var customer = new Customer(Session.DefaultSession);
bool fired = false;

customer.Changed += (sender, args) => fired = true;
customer.AgeToBeIngored++;

Assert.AreEqual(false, fired);
}


AgeToBeIngored property is decorated with XpoIgnoreAutomaticPropertyAttribute which allows you to control to which properties the aspect will be applied



Green light for this one!!! If still not convince then open up xaf and check if its undo feature still works that will convince you. I know because I did it to convince myself. PostSharp does its magic at compile time it inserts IL instructions directly to your assembly



I have prepare a sample project demonstrating that approach download it here EditorState



A good trick is to open your assembly with Reflector and see what PostSharp / XpoAutomaticProperties does behind the scenes



reflectorpng

Subscribe to XAF feed
Subscribe to community feed

DiggIt!

0 comments:

Post a Comment