Saturday, August 26, 2006

Lazy Loading Using .NET Remoting Proxy

One problem with a rich domain layer is that the reconsitution of aggregate objects from a persistence layer can take a long time.  Furthermore, quite frequently not all objects within the aggregate are even called upon during it's lifespan.  This means the time and elbow grease your application spent pulling data from the database and building up the domain object was pretty much in vain.

Performance efficiencies can be realized by deferring the reconstitution of the aggregate's internal objects until they are actually needed.  This is a Fowler EAA pattern called Lazy Loading.

 In .NET Lazy Loading is often implemented as an ugly mess of Properties such as:

public class Customer
{
private int id;
private string name;
private string description;
private bool isLoaded = false;

public int Id
{
get { return id; }
set { id = value; }
}

public string Name
{
get {
if(!isLoaded)
Load();
return name;
}
set {
if(!isLoaded)
Load();
name = value;
}
}

public string Description
{
get {
if(!isLoaded)
Load();
return description;
}
set {
if(!isLoaded)
Load();
description = value;
}
}
}

This flavour of Lazy Loading is called Lazy Initialization and has two main problems.  First is code duplication.  Any class that is lazy loaded first does a check to see if its been loaded and then makes a call to the database to actually load the object.  Ideally this sequence should be refactored and reused among all objects that are lazy loaded.

The second problem with this code is that domain objects become littered with code unrelated to their sole responsibility.  This impacts the communicability and simplicity of domain objects and makes the code more difficult to maintain.  I prefer my domain objects to be pure.  They should have no knowledge about how or when they or their data gets reconstituted.

Ideally, I would like an object that looks like a Customer (and can, in fact, be used as a Customer object) to sit as a proxy in the getter of my aggregate root.  This proxy can be passed around and will remain an empty shell of a Customer until one of the properties is requested at which point the proxy would fetch the data to reconstitute the Customer.

This flavour of lazy loading is called Virtual Proxy and can be achieved in .NET with the help of the Sytem.Remoting.Proxies namespace.

Essentially we can derive a proxy class from RealProxy that will intercept calls to the getters and setters of our domain object.  When the LazyLoadProxy receives a message for a getter it will first load the class from the database then continue to invoke the getter.  If the LazyLoadProxy receives a message for a setter it will first load the domain object then continue to invoke the setter.

public class LazyLoadProxy : RealProxy
{
private readonly IGhostable subject;

[PermissionSet(SecurityAction.LinkDemand)]
public LazyLoadProxy(int id, Type type) : base(type)
{
  subject = (IGhostable) Activator.CreateInstance(type);
  InitializeAsGhost(id);
}

private void InitializeAsGhost(int id)
{
  subject.Id = id;
}

[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
public override IMessage Invoke(IMessage msg)
{
  MethodCallMessageWrapper wrappedMessage = new MethodCallMessageWrapper((IMethodCallMessage) msg);
  MethodInfo invokedMethodInfo = (MethodInfo)   wrappedMessage.MethodBase;
InvokeStrategy strategy = GetInvokeStrategy(invokedMethodInfo);
return strategy.InvokeMethod(subject, invokedMethodInfo, wrappedMessage);
}

You'll notice in the above code that I've introduced the term Ghost.  Ghosting is just another flavour of lazy loading where you have a real object without any data (or partial data).  In this case, our ghost object knows its Id.  When it comes time to reconstitute the domain object our ghost object can do so by passing the Id into the persistence object.

Now, we will implement the IGhostable interface in the domain objects we want to lazy load.

public interface IGhostable
{
int Id { get; set; }
bool IsGhost { get; }
bool IsLoading { get; }
void MarkLoaded();
void MarkLoading();
void Load(IGhostable subject);
}

But to prevent duplication bloat in our code we can inherit from this Layer Supertype called GhostObject.  The GhostObject class is responsible for knowing when to reconstitute the subclass.  It looks a little like:

public abstract class GhostObject<T> : MarshalByRefObject where T : IGhostable
{
private readonly object GHOST = new object();
private readonly object LOADING = new object();
private readonly object LOADED = new object();
private object status;

protected GhostObject()
{
status = GHOST;
}

public bool IsGhost
{
get { return status == GHOST; }
}

public bool IsLoading
{
get { return status == LOADING; }
}

public void MarkLoaded()
{
status = LOADED;
}

public void MarkLoading()
{
status = LOADING;
}
public void Load(IGhostable subject)
{
ILoader<T> loader = LoaderRegistry.GetLoader<T>();
subject.MarkLoading();
loader.Load((T) subject);
subject.MarkLoaded();
}

public static T CreateGhost(int id)
{
return (T) new LazyLoadProxy(id, typeof(T)).GetTransparentProxy();
}
}

You should be aware of two things.  First, the GhostObject class inherits from MarshalByRefObject.  This is a prerequisite for the class to be used by RealProxy.  Also note that the GetTransparentProxy() method of RealProxy is getting called from the CreateGhost method.  This is what generates a class that is a proxy for the subclass.

Now, our Customer achieves a pure state of domain object zen.  As for me, having lost the clutter of lazy initialization from this object I can once again sleep soundly at night.

public class Customer: GhostObject<Customer>, IGhostable
{
private int id;
private string name;
private string description;

public int Id
{
get { return id; }
set { id = value; }
}

public string Name
{
get { return name; }
set { name = value; }
}

public string Description
{
get { return description; }
set { description = value; }
}
}

And pulling it all together results in a test that looks like:

[TestFixture]
public class CustomerTest
{
private Mockery mockery;
private ILoader<Customer> loader;

[SetUp]
public void SetUp()
{
mockery = new Mockery();
loader = (ILoader<Customer>) mockery.NewMock(typeof (ILoader<Customer>));
LoaderRegistry.RegisterLoader(loader);
}

[Test]
public void ShouldMarkAsNotGhost()
{
Customer Customer = Customer.CreateGhost(99);
Expect.Once.On(loader).Method("Load").With(Is.EqualTo(Customer)).Will(new LoadCustomerAction());
Assert.IsNotNull(Customer);
Assert.AreEqual("My Customer", Customer.Name);
Assert.IsFalse(Customer.IsGhost);
Assert.IsFalse(Customer.IsLoading);
mockery.VerifyAllExpectationsHaveBeenMet();
}

[Test]
public void ShouldMarkAsLoadedWhenPropertySet()
{
Customer Customer = Customer.CreateGhost(99);
Customer.Name = "Fred"
Assert.IsFalse(Customer.IsGhost);
Assert.IsFalse(Customer.IsLoading);
Assert.AreEqual("Fred", Customer.Name);
mockery.VerifyAllExpectationsHaveBeenMet();
}

}

As you can see from the test, to create a customer that will lazy load you just have to call the factory method Customer.CreateGhost().  So to achieve performance gains I can replace all reconstitutions of aggregate objects in the persistence layer with calls to DomainObject.CreateGhost() and have them lazy load automatically.

If you would like my sample code, complete with NUnit tests just drop me a line.

 
s