6

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.

3
  • You should read Fowler's Patterns of Enterprise Application Architecture. It contains several useful strategies for data access, including Lazy Loading. Commented Sep 23, 2015 at 15:45
  • if you use EntityFramework lazy initialization is switched on by marking property as virtual.
    – Szer
    Commented Sep 24, 2015 at 6:33
  • "So how should I let the Order know where to fetch the Volumes?" This question is unrelated to whether you load the data eagerly or lazily, and lies at the core of your problem. Lazy loading isn't the issue here, sourcing the volume data is the issue.
    – Flater
    Commented Aug 15 at 3:19

2 Answers 2

0

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?)

2
  • I don't understand your opening paragraph. It sounds like you're saying an anemic domain model is a design pattern but the link states why it's an anti pattern Commented Sep 24, 2015 at 20:30
  • The opening sentence is merely a reference my assumptions about the system in which the OP is programming. I will remove the first link, as I didn't preview it well enough for the connotations it might bring. Commented Sep 24, 2015 at 20:45
0

I'm fine with Order having reference to IVolumeRepository, but only under 2 conditions.

First is that all involved entities are under single Context. Eg. a class exists that represents current transaction that handles lifetime of all it's entities. So this Context needs to exist as long as you are manipulating those entities and newly fetched and created entities fall under this context. This is how ORMs do it.

Second is that the implementation of the IVolumeRepository is injected transparently when new instance of Order is either created or materialized from the database. This ensures that the domain has no knowledge about actual implementation of the repository. Again, this is how ORMs do it, because every materialized concrete instance of entity (ORMs mostly create their own concrete class for each entity through runtime code generation) has reference to Context.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.