It's called lazy loading because, like a lazy person, you are putting off doing something you don't want to. The opposite is Eager Loading, where you load something right away, long before you need it.
If you are curious why people might use lazy loading, consider an application that takes a LOOOOONG time to start. This application is probably doing a lot of eager loading... loading things from disk, and doing calculations and whatnot long before it is ever needed.
Compare this to lazy loading, the application would start much faster, but then the first time you need to do something that requires some long running load, there may be a slight pause while it is loaded for the first time. Thus, with lazy loading, you are amortizing the load time throughout the course of running your application... and you may actually save from loading things that the user may never intend to use.
A) If you're going to access the MarginCalculator through the Product domain object, you might as well cut out the middle man and let the DI/IOC container inject the MarginCalculator for you. You can even get rid of the MarginCalculatorAssembler because most DI/IOC containers do most of the boilerplate code of object construction for you.
B and C) It's very possible. In fact, here's how your code would look like if you used LinFu:
// No need to change the Transaction class
public class Transaction
{
private double _margin;
private Product _product;
private Client _client;
public double Margin { get; }
public Product Product { get; }
public Client Client { get; }
public Transaction(Product p, Client c)
{
_product = p;
_client = c;
}
public void CalculateMargin()
{
_margin = _product.MarginCalculator.CalculateMargin();
}
}
It would be nice if you could get a DI/IOC to inject the product and client instances into the constructor--but before we do that, you need to register the dependencies with the container. Here's how you do it with LinFu.IOC:
// Next, you'd have to tell LinFu to automatically register your product class:
[Factory(typeof(Product))]
public class ProductFactory : IFactory
{
object CreateInstance(IServiceRequest request)
{
// Grab a copy of the IRepository from the container
var repository = container.GetService>();
// Get the id (this assumes that your id is an Int32)
var id = (int)request.Arguments[0];
// Return the product itself
return repository.GetById(id);
}
}
// Do the same thing with the Client class
// (Note: I did a simple cut and paste to keep things simple--please forgive the duplication)
[Factory(typeof(Client))]
public class ClientFactory : IFactory
{
object CreateInstance(IServiceRequest request)
{
// Grab a copy of the IRepository from the container
var repository = container.GetService>();
// Get the id (this assumes that your id is an Int32)
var id = (int)request.Arguments[0];
// Return the client itself
return repository.GetById(id);
}
}
[Factory(typeof(Transaction))]
public class TransactionFactory : IFactory
{
object CreateInstance(IServiceRequest request)
{
// Note: Argument checking has been removed for brevity
var container = request.Container;
var arguments = request.Arguments;
var productId = (int)arguments[0];
var clientId = (int)arguments[1];
// Get the product and the client
var product = container.GetService(productId);
var client = container.GetService(clientId);
// Create the transaction itself
return new Transaction(product, client);
}
}
// Make this implementation a singleton
[Implements(typeof(MarginCalculator), LifecycleType.Singleton)]
public class ConcreteMarginCalculatorA : MarginCalculator
{
public override double CalculateMargin()
{
// Perform actual calculation
}
}
Once you have all that code compiled in one of your assemblies, here's all you need to do to load it into the container:
var container = new ServiceContainer();
container.LoadFrom(AppDomain.CurrentDomain.BaseDIrectory, "YourAssembly.dll");
...Now for the fun part. In order to create your transaction object with the given product and client ID, here's the call you need to make to LinFu.IOC's container:
int productId = 12345;
int clientId = 54321;
string serviceName = null;
// Not pseudocode :)
var transaction = container.GetService(serviceName, productId, clientId);
What makes this interesting is that despite the number of dependencies you might have, LinFu's IOC container will handle 90% of the boilerplate code for you so you don't have to do all this stuff on your own. The best part is that all the implementations above will all be determined/resolved at runtime.
You can practically swap implementations while the program is running, and you can even replace implementations without even recompiling your application. You can find more info here:
http://www.codeproject.com/KB/cs/LinFu_IOC.aspx
HTH :)
Best Answer
I suggest something different... Use a lazy load class :
Once you get it, you don't need to inject the dao in you object anymore :
When creating a Transcation object that is not bound to database :
When regenerating a Transaction from the database in the repository:
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