Question

I am currently implementing the repository pattern which allows me to pass my class and context into a general repository. This is working perfectly. The methods I am interested in are Get and Find. The find allows me to specify and "order", some "where" statements and some "include" statements. This is ideal.

public class GeneralRepository<TEntity> : IGeneralRepository<TEntity> where TEntity : class
{

    readonly GamesContext context;
    readonly DbSet<TEntity> db;

    public GeneralRepository(GamesContext existingContext)
    {
        context = existingContext;
        db = context.Set<TEntity>();
    }

    public TEntity Get(object id)
    {
        return db.Find(id);
    }

    public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
    {
        IQueryable<TEntity> query = db;

        if (filter != null)
        {
            query = query.Where(filter);
        }

        foreach (var includeProperty in includeProperties.Split
           (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        if (orderBy != null)
        {
           return orderBy(query).ToList();
        }
        else
        {
           return query.ToList();
        }
    }
}

My question however, when I use the Get method I would rather it return the full object graph. Lazy loading is turned off at the moment but to eager load I need to add the relevant includes, only this would defeat the object of doing a generic repository.

I know I could just use the find method however it would be much quicker to by default return all related data using the get method.

Any ideas how I can get all (eager load all) by default?

thanks.

Was it helpful?

Solution

This is a bad idea. Especially if you plan to build the application further. This is a road to performance bottlenecks and OutOfMemory exceptions.

And I found that generic repositories are also anti-pattern. With time you'll realise that you'll need to add more and more functions within that Get method and eventually it will just copy implementation of DbContext.

In fact, your current implementation does not serve any purpose of isolation. Why not just use DbContext directly?

I recommend dump the idea of Generic stuff and have small Query classes that retrieve only the entities you require in a controlled fashion, not everything for everyone. Have a look at this .

And to answer your question, Entity Framework does not have an option to provide IncludeAll. You can do a hack with reflection, but I'm not going to give solution here because it is just a bad practice.

OTHER TIPS

Here is a a good aproach to eager load just what you need using a generic repository pattern. Hope it can help someone, it's easy to use and simple enough to include in your code.

IRepository.cs:

public interface IRepository<TEntity> where TEntity : class
{
    IEnumerable<TEntity> GetAll(params Expression<Func<TEntity, object>>[] properties);
}

Repository.cs

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{    
    private readonly DbSet<TEntity> _dbset;

    public Repository(DbSet<TEntity> dbset)
    {
        _dbset = dbset;
    }

    public virtual IEnumerable<TEntity> GetAll(params Expression<Func<TEntity, object>>[] properties)
    {  
        if (properties == null) 
            throw new ArgumentNullException(nameof(properties));

        var query = _dbset as IQueryable<TEntity>; // _dbSet = dbContext.Set<TEntity>()

        query = properties
                   .Aggregate(query, (current, property) => current.Include(property));

        return query.AsNoTracking().ToList(); //readonly
    }
}

Some model to explain how to use:

public class User
{
    public int Id {get; set;}
    public string Name {get; set;}
    public List<Address> Address {get; set;}
    public List<Cart> Carts {get; set;}
}

How to use:

var repository = new Repository<User>();
var users = repository.GetAll(d => d.Address, d => d.Carts);

Ref: Link

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top