Lazy load and persistence ignorance
I often see questions about how to make lazy loads in entities, and wether using dependency injection for it.
The usual response is something like this :
class Entity
{
private Guid id;
private SubEntity subEntity;
private IDataService dataService;
public Entity(Guid id, IDataService dataService)
{
this.id = id;
this.dataService = dataService;
}
public SubEntity SubEntity
{
get
{
if (subEntity == null)
subEntity = dataService.GetSubEntity(id);
return subEntity;
}
}
}
As you can see, your entity is seriously tied to your persistence problem now. Even if it accesses the persistence layer through an interface, it is not persistence ignorant anymore.
The smell is that you cannot create an in memory instance of your entity anymore without worrying about data access.
Should you sill inject the data access service, or is it useless and you should leave it null ?
So believe me : do not inject data services in your entity.
Now, how can we still have the lazy load behavior ?
Through injection. But not dependency injection, I use execution inversion of control through delegate injection.
If you give to your entity a function that will return the value when asked, it's as if you gave the value.
Let's encapsulate this in a small class :
public class Lazy<T>
{
private T value;
private Func<T> loader;
public Lazy(T value)
{
this.value = value;
}
public Lazy(Func<T> loader)
{
this.loader = loader;
}
public 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);
}
}
Then you can use it like this in your entity :
class Entity
{
private readonly Guid id;
private readonly Lazy<SubEntity> subEntity;
public Entity(Guid id, Lazy<SubEntity> subEntity)
{
this.id = id;
this.subEntity = subEntity;
}
public Guid Id { get { return id; } }
public SubEntity SubEntity
{
get { return subEntity; } // implicit cast here
}
}
The code is more straight forward, the intent is clearly visible.
One benefit here, is that the subEntity field can be marked as readonly, that is a big improvement because our entity is really immutable now. Actually the Lazy<T> is mutable, but it behaves as an immutable value.
If your entity is not immutable, you can still leverage the Lazy<T> class :
class Entity
{
private readonly Guid id;
private Lazy<SubEntity> subEntity;
public Entity(Guid id, Lazy<SubEntity> subEntity)
{
this.id = id;
this.subEntity = subEntity;
}
public Guid Id { get { return id; } }
public SubEntity SubEntity
{
get { return subEntity; } // implicit cast here
set { subEntity = value; } // implicit cast here too
}
}
The last part is about how you use it when instantiating the object.
When creating a new instance in a factory (not linked to database) :
SubEntity subEntity = ...;
Guid id = ...;
Entity entity = new Entity(id, subEntity);
Here again, the implicit cast happen to pass the SubEntity as an already loaded Lazy<SubEntity>.
When binding the entity to the database :
class EntityBuilder
{
private IDataService dataService;
public EntityBuilder(IDataService dataService)
{
this.dataService = dataService;
}
public Entity GetEntity(Guid id)
{
return new Entity(
id,
new Lazy<SubEntity>(() => dataService.GetSubEntity(id)));
}
}
We can use a small helper method to make the instantiation cleaner :
public static class Lazy
{
public static Lazy<T> From<T>(Func<T> loader)
{
return new Lazy<T>(loader);
}
}
Then you can write :
return new Entity(
id,
Lazy.From(() => dataService.GetSubEntity(id)));
Now, the code that instantiate the Entity decides where the sub entity comes from.
The entity has become truly persistence ignorant.
Some would also advice not to use lazy load at all... this is still an option to consider !
Continued in Lazy loads and persistence ignorance (Part 2)