R – the proper way to inject a data access dependency for lazy loading

inversion-of-controllazy-loading

What is the proper way to inject a data access dependency when I do lazy loading?

For example I have the following class structure

class CustomerDao : ICustomerDao
  public Customer GetById(int id) {...}

class Transaction {
  int customer_id; //Transaction always knows this value
  Customer _customer = null;
  ICustomerDao _customer_dao;
  Customer GetCustomer() {
    if(_customer == null)
      _customer = _customer_dao.GetById(_customer_id);
    return _customer
  }

How do I get the reference to _customer_dao into the transaction object? Requiring it for the constructor seems like it wouldn't really make sense if I want the Transaction to at least look like a POCO. Is it ok to have the Transaction object reference the Inversion of Control Container directly? That also seems awkward too.

How do frameworks like NHibernate handle this?

Best Answer

I suggest something different... Use a lazy load class :

public class Lazy<T>
{
   T value;
   Func<T> loader;

   public Lazy(T value) { this.value = value; }
   public Lazy(Func<T> loader { this.loader = loader; }

   T Value
   {
     get 
    {
       if (loader != null)
       {
         value = loader();
         loader = null;
       }

       return value;
    }

    public static implicit operator T(Lazy<T> lazy)
    {
        return lazy.Value;
    }

    public static implicit operator Lazy<T>(T value)
    {
        return new Lazy<T>(value);
    }
}

Once you get it, you don't need to inject the dao in you object anymore :

public class Transaction
{
    private static readonly Lazy<Customer> customer;

    public Transaction(Lazy<Customer> customer)
    {
      this.customer = customer;
    }

    public Customer Customer
    {
       get { return customer; } // implicit cast happen here
    }
}

When creating a Transcation object that is not bound to database :

new Transaction(new Customer(..)) // implicite cast 
                                  //from Customer to Lazy<Customer>..

When regenerating a Transaction from the database in the repository:

public Transaction GetTransaction(Guid id)
{
   custmerId = ... // find the customer id 
   return new Transaction(() => dao.GetCustomer(customerId));
}

Two interesting things happen : - Your domain objects can be used with or without data access, it becomes data acces ignorant. The only little twist is to enable to pass a function that give the object instead of the object itself. - The Lazy class is internaly mutable but can be used as an immutable value. The readonly keyword keeps its semantic, since its content cannot be changed externaly.

When you want the field to be writable, simply remove the readonly keyword. when assigning a new value, a new Lazy will be created with the new value due to the implicit cast.

Edit: I blogged about it here :

http://www.thinkbeforecoding.com/post/2009/02/07/Lazy-load-and-persistence-ignorance