locked
Entity Framework + IRepository = Eager Loading impossible??? RRS feed

  • Question

  • Hi folks,

    I've been using EF for a project of mine for quite a while now. And for clean code reasons I'm currently trying to introduce the IRepository pattern. Unfortunately I encountered a very annoying problem.

    The current situation: 

    My entities (simplyfied:-):

    public class Article
    {
    	public string Name { get; set; }
    	public string Text { get; set; }
    	public IEnumerable<Tag> Tags { get; set; }
    }
    
    public class Tag
    {
    	public string Text { get; set; }
    }


    My generic repository (snippet):

    public class DbContextRepository<TEntity> : IRepository<TEntity>
    	where TEntity : class
    {
    	[...]
    	
    	TEntity IRepository<TEntity>.GetById<TId>(TId id)
    	{
    		return _dbSet.Find(id);
    	}
    	
    	[...]
    }


    Somewhere in my code a specific article is required:

    Article article;
    using (IUnitOfWork unitOfWork = _delCreateUnitOfWork())
    { 
    	article = unitOfWork.GetRepository<Article>().GetById(id);
    }
    
    [...]
    
    DoStuffWithTags(article.Tags);

    Within the unit of work context I can fetch the article required. Somewhere else in my code the article is processed. But when I want to access it's Tags, an ObjectDisposedException is fired:

    "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."

    Ok, the tags weren't fetched along with the article, therefore EF wants to lazy load them now. Without IRepository I would have done somthing like this:

    using (var context = CreateContext())
    {
       article = context.Articles.Include(a => a.Tags).Find(a => a.Id == Id);
    }

    Thus telling the framework it shall eager load also the article's Tags!

    But using IRepository this is not possible anymore and especially not within a generic repository. The generic repository doesn't know anything of Articles or Tags as it was build for any kind of entity. Even I implement a repository for each entity, I had to hard code which associations to eager load. That would probably work, but it's definitely not performant (and not pretty).

    So, what I would like to do is somethingl like that:

    Article article;
    using (IUnitOfWork unitOfWork = _delCreateUnitOfWork())
    { 
    	article = unitOfWork.GetRepository<Article>().Include(a => a.Tags).GetById(id);
    }

    That way eager loading is only done if really required.

    But how shall that be implemented?
    Is there any better way to do it?

    Thanks for your help,
    Dante

    Sunday, May 4, 2014 12:17 PM

Answers

  • The repository for each entity type should return a fixed-shape object graph, but it can contain related entities.

    I would probably just define a default shape for each eagerly-loaded entity in GetByID(). 

    Eg Order includes OrderDetails, and Article includes Tags.  If you want some non-default shape for the returned entity graph, bypass the IRepository

    David


    David http://blogs.msdn.com/b/dbrowne/

    • Marked as answer by Fred Bao Monday, May 12, 2014 7:34 AM
    Monday, May 5, 2014 5:50 PM

All replies

  • You could pass an argument of type Expression<Func<T, object>> to the generic Get method to specify which related entities to load. Please refer to the "Loading related entities" section of my blog post about how to implement a generic data access layer using Entity Framework for more information: http://blog.magnusmontin.net/2013/05/30/generic-dal-using-entity-framework/
    Sunday, May 4, 2014 5:45 PM
  • Yup,

    I've already considered enhancing the interface. It's probably the only solution to dynamically tell the framework which entities to eager load. The disadvantage of this solution is that it's now an interface specialized for the EF. At work we have a special serializer that would meet the requirements of the simple interface, but not the enhanced interface.

    Monday, May 5, 2014 5:07 PM
  • The repository for each entity type should return a fixed-shape object graph, but it can contain related entities.

    I would probably just define a default shape for each eagerly-loaded entity in GetByID(). 

    Eg Order includes OrderDetails, and Article includes Tags.  If you want some non-default shape for the returned entity graph, bypass the IRepository

    David


    David http://blogs.msdn.com/b/dbrowne/

    • Marked as answer by Fred Bao Monday, May 12, 2014 7:34 AM
    Monday, May 5, 2014 5:50 PM