C# – How to populate Lazy object from database

cdesign-patternslazy-initializationrepository

I have these classes:

public class Order
{
    private Lazy<IEnumerable<Volume>> _volumes;
    long ID { get; private set; }
    string Description { get; private set; }
    IEnumerable<Volume> Volumes { get { return _volumes.Value; } }
    //etc..
}
public class Volume
{
    long ID { get; private set; }
    string Description { get; private set; }
    //etc..
} 

I need to instantiate and populate several Orders. To supply the objects of these classes I have these Repositories:

public interface IOrderRepository
{
    Order GetByID(long ID);
    IEnumerable<Order> GetAll();
    Order Create(long ID, string Description);
    void Update(Order O);
    void Delete(Order O);
}
public interface IVolumeRepository
{
    Volume GetByID(long ID);
    IEnumerable<Volume> GetAll(Order O);
    Volume Create(long ID, string Description, Order O);
    void Update(Volume V);
    void Delete(Volume V);
}

I don't need to populate the Volumes of each Order upon instantiation, it would be best to implement lazy loading, in my opinion, as there can be a lot of Volumes.
So how should I let the Order know where to fetch the Volumes? I have this code that populates the Order it is instantiated:

//Interface
public interface IVolumeFetcher { IEnumerable<Volume> GetAll(Order O); }
//Order Constructor
public Order(long ID, string Description, IVolumeFetcher VF)
{
    _volumes = new Lazy<IEnumerable<Volume>>(() => VF.GetAll(this));
}

This gives the Order a reference to the Repository, it doesn't seem a good idea to me.
I could keep the Order's Volumes a simple List and fill it when needed:

public List<Volume> Volumes { get; private set; }

But this approach would be easily corrupted, any caller could add a Volume to the list and it wouldn't be persisted to the DB.
Btw, this is a three layered application, with a Order and Volume class for each layer, as well as a repository.

Best Answer

Assuming transaction script over objects and no ORM.

You don't want your object to have repository code. Treat Lazy<T> as you would any constructor parameter, so the IOrderRepository will be responsible for providing it as a constructed object to the Order constructor.

public Order(long id, string description, Lazy<IEnumerable<Volume>> volumes)
{
    ...
    _volumes = volumes;
}

If you are concerned about the repository reference sitting in the closure of Lazy<T> or needing to be disposed, since this usage is read-only, you could create and dispose an IVolumeRepository within the Lazy method:

// assuming IVolumeRepository is IDisposable or UOW
var volumes = new Lazy<IEnumerable<Volume>>(() =>
{
    //using (var repo = new ConcreteVolumeRepository())
    // or repo factory
    //using (var repo = volumeRepoFactoryFn())
    // or DI container
    using (var repo = DIContainer.Get<IVolumeRepository>())
    {
        return VF.GetAll(orderId))
    }
};

var order = new Order(orderId, description, volumes)

If your repository is a singleton (not that it should be), then I would just do:

//IVolumeRepository volumeRepo ...
var orderId = ...
var description = ...
var volumes = new Lazy<IEnumerable<Volume>>(() => volumeRepo.GetAll(orderId));

var order = new Order(orderId, description, volumes);

As a side note, your volume repository should load by order ID rather than order itself. I assume the IVolumeRepository.GetAll(Order O) method simply takes the ID property from the order, which is not useful to abstract away. If you want to make sure that it's an ID off an Order (versus any other kind of long), you can make OrderId a separate class or struct which wraps a long. That would accomplish the same purpose without causing a chicken-egg problem with loading Volumes. But I assume unit tests should provide sufficient verification without having to create a separate OrderId struct. (Why else would you use repository interfaces if not to unit test?)