0

I have a entity like:

public class Doctor : User
{
    public Doctor(string userName, string firstName, string lastName,
        string mobileNumber, string email, Sexes sex, Role myRole, DoctorExpertises expertise)
        : base(userName, firstName, lastName, mobileNumber, email, sex, myRole)
    {
        this.Expertise = expertise;
        this.Results = new List<Result>();
    }

    private Doctor()
    {
        this.Results = new List<Result>();
    }

    public void AddResult(Result result)
    {
        this.Results.Add(result);
    }

    public DoctorExpertises Expertise { get; private set; }

    private ICollection<Result> results;

    public virtual ICollection<Result> Results
    {
        get { return results; }
        private set { results = value; }
    }
}

And I have typical repository like:

public abstract class RepositoryBase<T> where T : class
{
    private DbContext dataContext;
    protected readonly IDbSet<T> dbset;

    protected RepositoryBase(IDatabaseFactory databaseFactory)
    {
        DatabaseFactory = databaseFactory;
        dbset = DataContext.Set<T>();
    }

    protected IDatabaseFactory DatabaseFactory
    {
        get;
        private set;
    }

    protected DbContext DataContext
    {
        get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
    }

    public virtual void Add(T entity)
    {
        dbset.Add(entity);
    }

    public virtual void Update(T entity)
    {
        dbset.Attach(entity);
        dataContext.Entry(entity).State = EntityState.Modified;
    }

    public virtual void Delete(T entity)
    {
        dbset.Remove(entity);
    }

    public virtual void Delete(Expression<Func<T, bool>> where)
    {
        IEnumerable<T> objects = dbset.Where<T>(where).AsEnumerable();
        foreach (T obj in objects)
            dbset.Remove(obj);
    }

    public virtual T GetById(long id)
    {
        return dbset.Find(id);
    }

    public virtual T GetById(string id)
    {
        return dbset.Find(id);
    }

    public virtual IEnumerable<T> GetAll()
    {
        return dbset.ToList();
    }

    public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
    {
        return dbset.Where(where).ToList();
    }

    public T Get(Expression<Func<T, bool>> where)
    {
        return dbset.Where(where).FirstOrDefault<T>();
    }
}

And I want lazy load one result from doctor with below test code:

[TestMethod]
public void TestShouldLoadPropertyIfLazyLoaded()
{
    // Act
    Doctor doctor = this.userRepository.Get(u => u.UserName == "soroosh") as Doctor;
    Result result = doctor.Results.FirstOrDefault();

    // Asserts
    Assert.IsNotNull(doctor);
    Assert.IsNotNull(result);
}

Unfortunately it doesn’t work correctly. doctor is not null, but result is null.

If I use eager loading with include method it loads correctly, but I definitely want to use lazy loading.

I have seen this question and other questions like this, but none of them wasn't useful, because all of them use eager loading to solve problem. Eager loading is not suitable for us, because in my main problem it seems eager loading is not possible.

What should I do?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sorosh_sabz
  • 2,356
  • 2
  • 32
  • 53
  • I'm sorry but I didn't understand what did you said. I say again doctor is not null. thank you :) – sorosh_sabz Nov 15 '13 at 15:33
  • OK I misunderstood You - sorry for that ;) I deleted the comment ;) Maybe You could try the following: Result result = doctor.Results.ToList().FirstOrDefault(); btw You should conssider splitting this into two separate tests ;) – Przemek Walendowski Nov 15 '13 at 15:45
  • Oh and where does userRepository come from? Are You hiting the database? – Przemek Walendowski Nov 15 '13 at 15:51
  • in initialize method I wrote some code like : this.userRepository = new UserRepository(this.databaseFactory); UserRepository is derived class from BaseRepository. – sorosh_sabz Nov 15 '13 at 17:44
  • Why would you need lazy loading? Are you sure `Result` isn't another AR? – JefClaes Nov 15 '13 at 18:12
  • @JefClaes I need lazy loading because I want to load entity property like Results. I am familiar with lazy loading and eager loading. eager loading isn't a suitable for [my problem](http://stackoverflow.com/questions/19983814/entity-framework-and-eager-loading-and-enterprise-application-with-ddd-approach). I am sorry but I don't know the meaning of AR. – sorosh_sabz Nov 15 '13 at 19:14
  • This looks a lot like my code, and mine works. But I don't have a private setter on my Collection... – Colin Nov 15 '13 at 20:15
  • `Result` might be a different aggregate root, since `Doctor` isn't enforcing any invariants on its `Results` collection. – JefClaes Nov 16 '13 at 11:24

2 Answers2

0

I reckon if you fully define your aggregate boundaries you don't really need to worry about this - you can always eagerly load when loading entities to perform domain operations because your relationships shouldn't cross aggregate boundaries. Your repository should therefore only be able to load and save a single aggregate of a given type, so that the behaviour on that aggregate can be invoked.

Don't get me wrong. You might still want to query your database to put data on the UI, etc., but you could just use a simple tool like LINQ to SQL, Dapper, etc. and whack the data straight out of the database into the UI, skipping all the ORM eager/lazy loading nonsense.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Neil Barnwell
  • 41,080
  • 29
  • 148
  • 220
  • Whacking the data directly from the database into the ui sounds like something that should be avoided at any cost if your code is to survive longer that the first demo. Orm could be nonsene but a properly layered and testable code begs for at least one layer of abstraction behind these two. – Wiktor Zychla Nov 16 '13 at 22:47
  • What problem is that layer of abstraction trying to solve? And how would tech like EF database-first, L2S or Dapper *not be* that layer of abstraction? You should be spending effort where it counts - on your domain logic and the invariants and business rules within. That's where the bulk of your tests should be. There's little point testing that you're able to read from a database, and really, how often are you likely to switch to a new rdbms anyway? I'm not saying write awful code with no exception handling etc, it should still be Clean Code, just it's not where the *goal* of a DDD app lies. – Neil Barnwell Nov 17 '13 at 09:34
0

This should just work. People including myself do this daily and it works. I see two possible culprits:

  • the dbcontext factory could possibly create a context where lazy loading is disabled
  • the setter of your Records property is private and the generated proxy entity could just not handle it properly. Just change it to public.

Check both, and focus on the second.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Wiktor Zychla
  • 47,367
  • 6
  • 74
  • 106
  • 1
    I would suggest going with `protected` rather than `public`, because lazy-loaded properties probably shouldn't have setters http://stackoverflow.com/a/14774042/150342. Then I'd add a backing field to avoid the "virtual call in constructor" code smell – Colin Nov 18 '13 at 11:39